[codeforces 1271D] Portals 反悔贪心算法
总目录详见https://blog.youkuaiyun.com/mrcrack/article/details/103564004
在线测评地址http://codeforces.com/contest/1271/problem/D
Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|
1271D - Portals | GNU C++11 | Accepted | 78 ms | 100 KB |
//此文https://blog.youkuaiyun.com/Missing_Cloud/article/details/103581785思路不错,摘抄如下
/*
之前的做法过于混乱(甚至我自己都觉得像是瞎蒙的),因此借鉴了其他神犇的题解仔细思考后发现可以使用反悔型贪心来做这道题。
思路为:
一路扫过去,每次都假定留守所有可留守的点,发现当前的士兵不够则从已留守的点中选取权值最小的放弃留守。
依然是基于 靠后留守更优 策略,预处理出每个点可留守的集合,遍历到该点直接将可留守的全部push到已留守集合中,再遍历下一个点。 在遍历点过程中发现士兵不够,pop已留守集合,如果pop空了还不够,说明游戏失败。
代码一起放到后面了。自我感觉注释够详细了。
*/
//样例通过,提交AC.2019-12-22 16:03
#include <cstdio>
#include <queue>
#include <vector>
#define maxn 5010
using namespace std;
int a[maxn],b[maxn],c[maxn],last[maxn],head[maxn],tot=0;//last[i]记录i最后能在哪里被留守,//head[i]存储该点可以向前留守什么点
int N,M,K;
priority_queue<int,vector<int>,greater<int> > q;
struct node{
int to,next;
}e[maxn];
void add_edge(int u,int v){
tot++,e[tot].to=v,e[tot].next=head[u],head[u]=tot;
}
void init(){
int i,u,v;
scanf("%d%d%d",&N,&M,&K);
for(i=1;i<=N;i++)scanf("%d%d%d",&a[i],&b[i],&c[i]),last[i]=i;
for(i=1;i<=M;i++)scanf("%d%d",&u,&v),last[v]=last[v]>u?last[v]:u;//last[v]取更大者,因为越后留守一定越好//此处错写for(i=i;i<=M;i++)scanf("%d%d",&u,&v),last[v]=last[v]>u?last[v]:u;成查了会
for(i=1;i<=N;i++)add_edge(last[i],i);//此处错写成for(i=1;i<=N;i++)add_edge(last[v],v);造成了linux下的段错误
}
int main(){
int i,ed,v,ans=0;
init();
for(i=1;i<=N;i++){
while(K<a[i]&&!q.empty())K++,q.pop();//不够的话,从已留守的城堡中取出权值最小的放弃
if(K<a[i]){printf("-1\n");return 0;}//如果取完还是不够,那没办法了,输出-1
K+=b[i];//占领当前点
for(ed=head[i];ed;ed=e[ed].next)//往前留守,这里不用考虑当前点,因为当前点要么也在vector中,要么在后面可以被留守
v=e[ed].to,q.push(c[v]),K--;//可以直接全部push到留守集合中,等后面再反悔,不用考虑会不会让k<0
}
while(K<0&&!q.empty())K++,q.pop();//最后一步后还要判断k是否大于0进行一次反悔//此处错写成while(K<a[i]&&!q.empty())K++,q.pop();
if(K<0){printf("-1\n");return 0;}//此处错写成if(K<a[i]){printf("-1\n");return 0;}
while(!q.empty())ans+=q.top(),q.pop();//统计已留守点的权值和
printf("%d\n",ans);
return 0;
}