codeforces733-C. Epidemic in Monstropolis 贪心加链表
题意
现在有一个怪兽序列a[i],权值大的怪兽可以吃权值小的怪兽,吃完之后权值大的怪兽的权值会变成两者权值的和,相邻的怪兽才能吃
吃完之后,位置合并,队列前移,从左到右重新编号,重复这一过程,
然后给你一个怪兽序列b[i],问你a[i]怎么操作能变成b[i],如果能操作,输出操作序列,如果不能操作就输出NO
难点一,划分序列,由于b[i]的概念类似于前缀和,一旦b[i]是确定的数,那么a[i]序列的划分就是唯一的,b[i]和a[i]的和相等
b[i]序列实际上就是对序列a[i]的唯一划分
难点二,如何判定由b[i]划分的小区间一定能全部合并?我们的办法是,找最容易满足的条件,如果你能养起来尽可能大的怪兽
,一旦它成为了最大值,那么这个小区间一定是可以合并的,这个贪心策略看起来是显然的(笑,所以我们每次尽量合成最大的怪兽
如果这样都合并不了整个区间,那么这个区间一定是不可合并的,我表示不会严格数学证明
难点三,如果用数组这个数据结构,我发现合并这个操作,我完全不知道怎么写,加上vis数组?哪个被合并就哪个记为被访问?
那么我们每次要找到两个没有被访问且连续的元素然后合并权值,操作vis,也不是不行,我觉得这个写法太tm恶心了
我选择用链表,权值用a[i]存,nxt[i]存储每个元素的下一跳,合并操作就变成了连跳操作,nxt[i]=nxt[nxt[i]],
但是对于数序号来说,我们要另外写一个函数来O(n),获取该元素的rank,还好n比较小
难点三、输出最终的答案序列
我用了vector,和结构体存储了位置了操作字符,不是很简单,有机会去看看大神们是怎么写的
难点四、考虑b[i]序列是否是合法序列,少考虑了两种,这个太致命了,一开始我是有想法的,后来忘了填坑了,以后觉得不放心的
地方都要加一个注释才行,具体坑点详见代码
难点五、注意合并操作的更新位置,要想记录当前操作,需要先记录再更新,更新后再记录就是错的了,不是我们想要的信息
#include <cstdio> #include <vector> #define ll long long const int maxn=507; int n,k; int a[maxn],b[maxn],flag[maxn]; int nxt[maxn]; struct node{ int pos; char type; //0 left 1 right };//结构体后面忘了加分号 std::vector<node> ans; void init(int left,int right){ register int i; for(i=left;i<right;++i){ nxt[i]=i+1; } nxt[right]=-1; } bool check(int left,int right){ if(nxt[left]!=-1) return false; return true; } int findIndex(int left,int index){ int cnt=0; register int i; for(i=left;i!=-1;i=nxt[i]){ cnt++; if(index==i){ return cnt; } } return -1; //error } int solve(int left,int right,int pre){ ll mx=-1; int index,first=1; register int i; for(i=left;i!=-1;i=nxt[i]){ if(nxt[i]!=-1&&a[i]!=a[nxt[i]]){ if(first){ first=0; mx=a[i]+a[nxt[i]]; index=i; } else{ ll temp=a[i]+a[nxt[i]]; if(temp>mx){ mx=temp; index=i; } } } } if(mx!=-1){ int pos1=findIndex(left,index); int pos2=findIndex(left,nxt[index]); if(a[index]>a[nxt[index]]){ ans.push_back((node){pre+pos1,'R'}); } else{ ans.push_back((node){pre+pos2,'L'}); } a[index]+=a[nxt[index]];//这两句话位置不对,之前做的早了 nxt[index]=nxt[nxt[index]]; } return mx; } int main(){ scanf("%d",&n); register int i; ll suma=0,sumb=0; for(i=0;i<n;++i){ scanf("%d",a+i); suma+=a[i]; } scanf("%d",&k); for(i=0;i<k;++i){ scanf("%d",b+i); sumb+=b[i]; } if(suma!=sumb) { printf("NO\n"); return 0; } ll temp=0; int cur=0,cnt=0; for(i=0;i<n;++i){ temp+=a[i]; if(temp==b[cur]){ flag[cnt++]=i; ++cur; temp=0; } else if(temp>b[cur]){ printf("NO\n");//NO 打成 No return 0; } } if(cur!=k) { printf("NO\n"); return 0; } register int j; int No=0; for(i=0;i<cnt;++i){ int left,right=flag[i]; if(i==0){ left=0; } else{ left=flag[i-1]+1; } init(left,right); while(!check(left,right)){ int p=solve(left,right,i); if(p==-1){ No=1; break; } } if(No){ break; } } if(No) printf("NO\n"); else{ printf("YES\n"); for(i=0;i<ans.size();++i){ node t=ans[i]; printf("%d %c\n",t.pos,t.type); } } return 0; }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步