 /**//*
題意:從上到下為1到N層,每一層三個值Wi,Li,Pi,分別表示已有水量,該層容量,手動放水價格
如果某一層的水超過Li,就會自動將全部水往下泄;也可以手動放水
問要使第N層水泄,至少的代價

一個必要條件:
如果第p層被手動放水,在其下的水都要被放走(自動或手動),即水不可能斷流!

可以枚舉最高放水點p,然后向下計算代價即可O(n^2) n<=15000 TLE
代價的計算:
i
如果層i需要手動放水,必須 ∑W[j]<=L[i]
j=p
剛才是打算枚舉p,然后每次都計算代價,可不可以不用每次都重新計算呢?
對于最高點為p的,其下層需要放水的集合 S[p]={i | ∑W[j]<=L[i]}
而對于p+1的,以p為最高點時層i如果需要手動放水,則以p+1為最高點時也必然要手動放水
因為∑W[j]減小了 即 S[p]∈S[p+1] , i>=p+1 ★★★

i
設A[i] = ∑W[j]-L[i] ,對A[i]排序,這時的A[i]其實就是p=1時的情況了
j=1
推到p=2,只需看A[i]-W[1]是否<=0 是的話表示需要手動放水
推到p=3,只需看A[i]-W[1]-W[2]


用pos記錄目前需要手動放水的 A[pos]
如果在枚舉p時,pos前的需要手動放水,則枚舉p+1時,pos前的也需要手動放水
所以pos這個指針不用回退的

法一:可以按照A[] sort一下,然后從p=1開始推 .
Sort完后保證了前面的算過的對于后面的也是有效的
法二:由S[p]∈S[p+1] , i>=p+1
可以用一個最大堆來做,對于最高點p,堆存的是p及之下的需要手動放水的層
i從N往前枚舉
維護:i+1->i時,加入i,并將堆里不需要手動放水的去掉,得到最終的代價了
啟示:有單調性的,排序后就不用重復計算了!!
跟ZJOI 2010 Base 一樣的思想,排序后算,指針不用回退!
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

const int MAXN = 15010;
const int INF = 1000000000;

struct Level
  {
int W,L,P;
int key;
bool operator<(const Level &B)const
 {
return key<B.key;
}
};

Level levels[MAXN];

bool vi[MAXN];
int id[MAXN];

bool cmp(const int &a,const int b)
  {
return levels[a].key<levels[b].key;
}

int main()
  {
//freopen("in","r",stdin);

int N;
while(~scanf("%d",&N))
 {
int totalW = 0,nowCost = 0;
//fill(vi+1,vi+1+N,false);
for(int i=1;i<=N;i++)
 {
id[i]=i;
scanf("%d%d%d",&levels[i].W,&levels[i].L,&levels[i].P);
totalW += levels[i].W;
levels[i].key = totalW-levels[i].L;
//if(levels[i].key<=0)//
//{
// vi[i]=true;
// nowCost+=levels[i].P;
//}
}

//sort(id+1,id+1+N,cmp);

//int bestP = 1,bestCost = nowCost;
//int subW=0,pos=1;
//for(int p=2;p<=N;p++)
//{
// subW+=levels[p-1].W;
// if(vi[p-1])//如果p之前的被算進nowCost來的話要減去
// nowCost-=levels[p-1].P;

// while(pos<=N&&levels[id[pos]].key-subW<=0)
// {
// if(!vi[id[pos]]&&id[pos]>=p)//p作為最高頂點時,層i需要手動放水,則加上代價
// {
// vi[id[pos]]=true;
// nowCost+=levels[id[pos]].P;
// }
// pos++;
// }
// if(nowCost<bestCost)
// {
// bestCost = nowCost;
// bestP = p;
// }
//}



 /**//******************************************************/
int bestCost = INF,bestP;
priority_queue<Level> PQ;
for(int i=N;i;i--)
 {
totalW-=levels[i].W;
PQ.push(levels[i]);
nowCost+=levels[i].P;
while(!PQ.empty()&&PQ.top().key-totalW>0)//>0 不用放水的去掉
 {
nowCost-=PQ.top().P;
PQ.pop();
}
if(nowCost<bestCost)
 {
bestCost = nowCost;
bestP = i;
}
}
 /**//******************************************************/

int addW = 0;
for(int i=bestP;i<=N;i++)
 {
addW+=levels[i].W;
if(addW<=levels[i].L)
printf("%d\n",i);
}
}
return 0;
}
|
|
常用鏈接
隨筆分類
Links
搜索
最新評論

|
|