NOI2013 小Q的修炼 题解
来源:互联网 发布:淘宝c店开通花呗 编辑:程序博客网 时间:2024/05/01 16:58
感觉这种题答都没有题解,而我又想在这里保存一下代码,就写一份题解咯。
【题意】
一共有M个变量。
有一系列的操作,按顺序标为1~N。操作分为三种:
①普通操作:将一个变量加上一个量。
②条件跳转:给出两个量A、B和两个编号P、Q。
如果A<B那么跳到编号为P的操作,否则跳到编号为Q的操作。
③选择跳转:给出两个编号P、Q。在P和Q中任选一个跳过去。
注意以上的“量”可以是常量,也可以是目前某一个变量的值。
如果什么时候跳转到的编号不在1~N之间,则视为退出游戏。
你要给出选择跳转的决策,使退出游戏的时候第一个变量的值尽量的大。
(数据隐藏条件:跳转满足拓扑序)
【Case1和Case2】
C1的决策只有20个,直接DFS一下。
(顺便要写一个比较DJ的读入和模拟器)
发现C2跑了几秒也跑出来了QAQ。
#include<bits/stdc++.h>#define N 1005#define x first#define y second#define mk make_pairusing namespace std;typedef pair<bool,int> Pair;int P[N],Q[N],sign[N],ch[N],ret[N];Pair A[N],B[N];char str[N][3];int n,m,ans;Pair read(){ char tmp[10];int d; scanf("%s%d",tmp,&d); if (tmp[0]!='c') fprintf(stderr,"%s %d\n",tmp,d); if (tmp[0]=='c') return mk(0,d); return mk(1,d);}int Read(){ char tmp[10]; scanf("%s",tmp); return tmp[0]=='+'?1:-1;}void DFS(int k,vector<int>now){ //fprintf(stderr,"%d\n",k); for (;k<=n&&str[k][0]!='s';){ if (str[k][0]=='v') now[A[k].y]+=(B[k].x?now[B[k].y]:B[k].y)*sign[k],k++; if (str[k][0]=='i'){ int t1=A[k].x?now[A[k].y]:A[k].y; int t2=B[k].x?now[B[k].y]:B[k].y; if (t1<t2) k=P[k];else k=Q[k]; } } if (k>n){ if (now[1]>ans){ freopen("train2.out","w",stdout); fprintf(stderr,"%d\n",ans); for (int i=1;i<=*ch;i++) printf("%d ",ch[i]); ans=now[1]; } }else{ ch[++*ch]=1;DFS(P[k],now);--*ch; ch[++*ch]=2;DFS(Q[k],now);--*ch; }}int main(){ freopen("train2.in","r",stdin); freopen("train2.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++){ scanf("%s",str[i]);int t; if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read(); if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]); if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]); fprintf(stderr,"%d %s\n",i,str[i]); } fprintf(stderr,"%s\n",str[n]); vector<int>now; for (int i=0;i<=m+2;i++) now.push_back(i); DFS(1,now); }
【Case3】
观察数据,发现可以分成一些互不干扰的块。
每一个块里有10个决策,每个决策会增加或减少编号为3~12的变量。
然后块的结尾会依据这3~12的变量给最终要求的1变量增加或减少值。
为什么称为“互不干扰”呢?因为每一块结尾都会把3~12的变量都清空。
然后只要对每一个块分别爆搜,把答案合并起来即可。
//#include<bits/stdc++.h>#include<stdio.h>#include<algorithm>#include<cstring>#include<vector>#include "Windows.h"#define N 350005#define x first#define y second#define mk make_pairusing namespace std;typedef pair<bool,int> Pair;int P[N],Q[N],sign[N],ch[66],ret[66],now[66];Pair A[N],B[N];char str[N][3];int n,m,ans,best,st;Pair read(){ char tmp[10];int d; scanf("%s%d",tmp,&d); if (tmp[0]=='c') return mk(0,d); return mk(1,d);}int Read(){ char tmp[10];scanf("%s",tmp); return tmp[0]=='+'?1:-1;}void DFS(int x){ //fprintf(stderr,"%d ",x); if (x>10){ int get=0,b=10*12+st; for (int i=b;i<b+170;i++){ //fprintf(stderr,"%d\n",i); if (str[i][0]=='i'){ int t1=A[i].x?now[A[i].y]:A[i].y; int t2=B[i].x?now[B[i].y]:B[i].y; if (t1<t2) i=P[i]-1;else i=Q[i]-1; }else if (A[i].y==1) get+=sign[i]*(B[i].x?now[B[i].y]:B[i].y); } if (get>best) best=get,memcpy(ret,ch,sizeof(ch)); return; } ch[x]=2;DFS(x+1); int b=st+(x-1)*12; for (int i=1;i<=10;i++) now[i+2]+=B[b+i].y; ch[x]=1;DFS(x+1); for (int i=1;i<=10;i++) now[i+2]-=B[b+i].y;}int main(){ freopen("train3.in","r",stdin); freopen("train3.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++){ scanf("%s",str[i]);int t; if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read(); if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]); if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]); }fprintf(stderr,"Start\n"); for (st=1;st<=n;st+=170){ best=0; for (int i=3;i<=12;i++) now[i]=0; DFS(1); ans+=best;fprintf(stderr,"%d %d\n",st,best); for (int k=1;k<=10;k++) printf("%d ",ret[k]); }fprintf(stderr,"%d\n",ans); return 0;}
【Case4~6】
发现只有两个变量。
2变量的增减都比较小,而且所有的条件跳转都只和2变量有关。
这就可以想到按顺序DP下去,每次记录当前2变量的值是多少即可。
最后再回去输出方案。说起来比较轻松,实际还是有很多细节的。
为了方便,DP的意义可以从“做完第i个操作,2变量的值为k”改成“做第i个操作之前,2变量的值为k”这样。
写完之后大概改一下也可以过第5个点。
//#include<bits/stdc++.h>#include<stdio.h>#include<algorithm>#include<cstring>#include<vector>#define N 10005#define x first#define y second#define mk make_pairusing namespace std;typedef pair<bool,int> Pair;int P[N],Q[N],sign[N];Pair A[N],B[N];char str[N][3];struct data{ int v,prei,prek,ch; void Min(int _v,int _ch,int _prei,int _prek){ if (_v<=v) return; v=_v;ch=_ch;prei=_prei;prek=_prek; } int operator < (const data &b)const{return v<b.v;}}F[6005][N],ans;int n,m;Pair read(){ char tmp[10];int d; scanf("%s%d",tmp,&d); if (tmp[0]=='c') return mk(0,d); return mk(1,d);}int Read(){ char tmp[10]; scanf("%s",tmp); return tmp[0]=='+'?1:-1;}int Case=0;void DFS(int i,int k){ //fprintf(stderr,"%d %d\n",i,k); //if ((++Case)==20) return; if (i<=2) return; DFS(F[i][k].prei,F[i][k].prek); if (F[i][k].ch>-1) printf("%d ",F[i][k].ch);}int main(){ freopen("train5.in","r",stdin); freopen("train5.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++){ scanf("%s",str[i]);int t; if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read(); if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]); if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]); } //F[i][k] 做第i行之前,v2的值为k的最大的v1的值 //for (int i=0;i<=n+1;i++) F[i]=f[i]+N/2;int bug=0; for (int i=0;i<=n+1;i++) for (int k=0;k<N;k++) F[i][k].v=-1e9; F[2][5000].v=0;ans.v=-1e9; for (int i=2;i<=n;i++) for (int k=0;k<N;k++) if (F[i][k].v>-1e9){ //printf("%d %d %d\n",i,k,F[i][k].v); if (str[i][0]=='v'){ int newk=k,newv=F[i][k].v; if (A[i].x&&A[i].y==2) newk+=sign[i]*B[i].y; else if (A[i].x&&A[i].y==1) newv+=sign[i]*B[i].y; //if (i==6) //fprintf(stderr,"%d %d\n",newv,newk); F[i+1][newk].Min(newv,-1,i,k); }else if (str[i][0]=='i'){ //if (bug) fprintf(stderr,"%d %d\n%d %d\n",A[i].x,A[i].y,B[i].x,B[i].y); int t1=A[i].x?k:A[i].y; int t2=B[i].x?k:B[i].y,newi; //if (bug) fprintf(stderr,"%d %d\n",t1,t2); if (t1<t2) newi=P[i];else newi=Q[i]; //if (bug) fprintf(stderr,"Newi:%d\n",newi); if (i==8) fprintf(stderr,"%d %d %d %d\n",t1,t2,newi,F[i][k].v); if (newi>=1&&newi<=n) F[newi][k].Min(F[i][k].v,-1,i,k); else ans=max(ans,(data){F[i][k].v,i,k}); }else { if (P[i]>=1&&P[i]<=n) F[P[i]][k].Min(F[i][k].v,1,i,k); else ans=max(ans,(data){F[i][k].v,i,k}); if (Q[i]>=1&&Q[i]<=n) F[Q[i]][k].Min(F[i][k].v,2,i,k); else ans=max(ans,(data){F[i][k].v,i,k}); } } fprintf(stderr,"Type1Ans:%d %d\n",ans.v,n); for (int k=0;k<N;k++) ans=max(ans,(data){F[n+1][k].v,n+1,k}); fprintf(stderr,"Type2Ans:%d\n",ans.v); DFS(ans.prei,ans.prek);}
【Case7~8】
首先和之前的一个点差不多,发现这些操作可以分成几组,每组格式类似。
发现每组的开头有些和2变量有关的判断:若2的值怎么怎么样强制你跳到后面一块。
否则的话,你也有对这一块的决策权:即你仍然有机会能跳出这一块。
如果你跳进这一块的话,可以像之前爆搜的那样搜出一些变量的取值。
在最外面再套一个DP,记录变量2的状态、决策是否进去某一块啥的。
找方案的时候可能更加麻烦。
//#include<bits/stdc++.h>#include<stdio.h>#include<algorithm>#include<cstring>#include<vector>#include "Windows.h"#define N 35005#define M 1005#define x first#define y second#define mk make_pairusing namespace std;typedef pair<bool,int> Pair;int ret[205][21],best[205];int P[N],Q[N],sign[N],ch[21],now[66];Pair A[N],B[N];char str[N][3];int F[N][M],pre[N][M],walk[N][M],extra[N][M];int n,m,ans,st,i,k,newk,tmp,tot;Pair read(){ char tmp[10];int d; scanf("%s%d",tmp,&d); if (tmp[0]=='c') return mk(0,d); return mk(1,d);}int Read(){ char tmp[10];scanf("%s",tmp); return tmp[0]=='+'?1:-1;}void DFS(int x){ //fprintf(stderr,"%d ",x); if (x>10){ int get=0,b=st+12*10; for (int i=b;i<st+175;i++){ //fprintf(stderr,"%d\n",i); if (str[i][0]=='i'){ int t1=A[i].x?now[A[i].y]:A[i].y; int t2=B[i].x?now[B[i].y]:B[i].y; if (t1<t2) i=P[i]-1;else i=Q[i]-1; }else if (A[i].y==1) get+=sign[i]*(B[i].x?now[B[i].y]:B[i].y); } if (get>best[tot]) best[tot]=get,memcpy(ret[tot],ch,sizeof(ch)); return; } ch[x]=2;DFS(x+1); int b=st+(x-1)*12; for (int i=1;i<=10;i++) now[i+2]+=B[b+i].y; ch[x]=1;DFS(x+1); for (int i=1;i<=10;i++) now[i+2]-=B[b+i].y;}void BACK(int i,int k){ if (i<=1) return; fprintf(stderr,"%d %d %d %d %d\n",i,k,F[i][k],walk[i][k],extra[i][k]); BACK(i-1,pre[i][k]); if (walk[i][k]!=-1) printf("%d ",walk[i][k]),assert(walk[i][k]>0); if (extra[i][k]) for (int c=1;c<=10;c++) printf("%d ",ret[extra[i][k]][c]);}int main(){ freopen("train8.in","r",stdin); freopen("train8.out","w",stdout); scanf("%d%d",&n,&m); for (i=1;i<=n;i++){ scanf("%s",str[i]);int t; if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read(); if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]); if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]); }fprintf(stderr,"Start\n"); for (st=6;st<=n;st+=175){ best[++tot]=0; assert(str[st][0]=='s'); for (i=3;i<=12;i++) now[i]=0; DFS(1); fprintf(stderr,"%d %d\n",st,best[tot]); for (k=1;k<=10;k++) assert(ret[tot][k]>0); } for (i=0;i<=tot+1;i++) for (k=0;k<M;k++) F[i][k]=-1e9; F[1][1000]=0;assert(tot==200); for (i=1;i<=tot;i++) for (k=0;k<M;k++) if (F[i][k]>-1e9){ //fprintf(stderr,"%d %d %d\n",i,k,F[i][k]); st=1+(i-1)*175+1; assert(str[st][0]=='i'&&A[st].y==2); if (k<B[st].y){ if (F[i][k]>F[i+1][k]) F[i+1][k]=F[i][k],pre[i+1][k]=k,walk[i+1][k]=-1,extra[i+1][k]=0; continue; }st+=2;assert(str[st][0]=='s'); if (F[i][k]>F[i+1][k]) F[i+1][k]=F[i][k],pre[i+1][k]=k,walk[i+1][k]=2,extra[i+1][k]=0; ++st;assert(A[st].y==2&&B[st].x==0); //fprintf(stderr,"%d %d\n",sign[st],B[st].y); newk=k+sign[st]*B[st].y; if (newk>k) fprintf(stderr,"%d %d\n",i,k); assert(newk<=k); //fprintf(stderr,"%d\n",F[i][k]+best[i]); if (F[i][k]+best[i]>F[i+1][newk]) F[i+1][newk]=F[i][k]+best[i],pre[i+1][newk]=k,walk[i+1][newk]=1,extra[i+1][newk]=i; } fprintf(stderr,"%d %d\n",F[tot][500],F[tot+1][500]); for (k=0;k<M;k++) if (F[tot+1][k]>ans) ans=F[tot+1][k],tmp=k; fprintf(stderr,"%d\n",ans); BACK(tot+1,tmp); return 0;}
【Case9~10】
这两个点感觉和之前的特别像。
但是我用之前的程序跑怎么也跑不过。后来发现了一些坑。
首先,循环节的长度稍微变化了一下。
然后假设循环节的长度为L,我发现操作数竟然不是L的倍数!
这是怎么回事呢?大概看一下,发现几千行的一个地方不是按照原来的规律在变化。
可以称这些地方为"噪点”。
如果再找出一行“噪点”,就可以大概归纳出其性质,然后把它们删掉即可。
对于删掉后的数据我还是搞了很长时间:显然我把每一条操作重新编号了。
因为在跳转的时候,编号还是会按照原先数据的编号,所以要搞一个比较麻烦的映射。
此外我记得还有很多细节,比如在块之间跳跃的时候,好像还可以往后跳好几块啥的,总之特别坑。
#include<bits/stdc++.h>#include<stdio.h>#include<algorithm>#include<cstring>#include<vector>#include "Windows.h"#define N 35005#define M 1005#define x first#define y second#define mk make_pairusing namespace std;typedef pair<bool,int> Pair;int ret[205][21],best[205],S[1005];int P[N],Q[N],index[N],pos[N],sign[N],ch[21],now[66];Pair A[N],B[N];char str[N][3];int F[N][M],prei[N][M],prek[N][M],walk[N][M],extra[N][M];int n,m,ans,st,i,k,newk,newi,tmpi,tmpk,tot,j;Pair read(){ char tmp[10];int d; scanf("%s%d",tmp,&d); if (tmp[0]=='c') return mk(0,d); return mk(1,d);}int Read(){ char tmp[10];scanf("%s",tmp); return tmp[0]=='+'?1:-1;}int bug=0;void DFS(int x){ if (x>10){ if (bug) assert(str[st+12*10][0]=='i'&&A[st+12*10].y==3); int get=0,b=st+12*10; for (int i=b;i<st+174;i++){ //fprintf(stderr,"%d\n",i); if (str[i][0]=='i'){ int t1=A[i].x?now[A[i].y]:A[i].y; int t2=B[i].x?now[B[i].y]:B[i].y; if (t1<t2) i=P[i]-1;else i=Q[i]-1; }else if (A[i].y==1) get+=sign[i]*(B[i].x?now[B[i].y]:B[i].y); }//if (bug) fprintf(stderr,"%d\n",get); if (get>best[tot]) best[tot]=get,memcpy(ret[tot],ch,sizeof(ch)); return; } ch[x]=2;DFS(x+1); int b=st+(x-1)*12; for (int i=1;i<=10;i++) now[i+2]+=B[b+i].y; ch[x]=1;DFS(x+1); for (int i=1;i<=10;i++) now[i+2]-=B[b+i].y;}void BACK(int i,int k){ if (i<=1) return; //fprintf(stderr,"%d %d %d %d %d\n",i,k,F[i][k],walk[i][k],extra[i][k]); BACK(prei[i][k],prek[i][k]); //fprintf(stderr,"%d\n",i); if (walk[i][k]!=-1) printf("%d ",walk[i][k]),assert(walk[i][k]>0); if (extra[i][k]) for (int c=1;c<=10;c++) printf("%d ",ret[extra[i][k]][c]);}int calc(int x){for (int i=1;i<=*S;i++)if (S[i]==x){int j=i;for (;j<*S&&S[j+1]==S[j]+1;++j);x=S[j]+1;break; }x-=lower_bound(S+1,S+*S+1,x)-(S+1);return x;}int main(){ freopen("train10.in","r",stdin); scanf("%d%d",&n,&m);n-=8;int last=0; for (i=1,j=1;i<=n;i++,j++){ scanf("%s",str[i]);int t;index[i]=j; if (str[i][0]=='v') scanf("%d",&t),A[i]=mk(1,t),sign[i]=Read(),B[i]=read(); if (str[i][0]=='i') A[i]=read(),B[i]=read(),scanf("%d%d",&P[i],&Q[i]); if (str[i][0]=='s') scanf("%d%d",&P[i],&Q[i]); if (i%174==2&&i>10) if (!A[i].x) assert(str[i][0]!='s'),i--,n--,S[++*S]=j; }printf("%d ",n); for (i=1;i<=n;i++) P[i]=calc(P[i]),Q[i]=calc(Q[i]); for (st=6;st<=n;st+=174){ best[++tot]=-1e9; assert(str[st][0]=='s'); for (i=3;i<=12;i++) now[i]=0; DFS(1); //fprintf(stderr,"%d %d\n",st,best[tot]); for (k=1;k<=10;k++) assert(ret[tot][k]>0);//fprintf(stderr,"%d ",ret[tot][k]); }assert(tot==200); bug=0;tot=0; for (st=2;st<=n;st+=174) pos[st]=++tot; for (i=0;i<=tot+1;i++) for (k=0;k<M;k++) F[i][k]=-1e9; F[1][1000]=0; for (i=1;i<=tot;i++) for (k=0;k<M;k++) if (F[i][k]>-1e9){ //fprintf(stderr,"%d %d %d\n",i,k,F[i][k]); st=1+(i-1)*174+1; assert(str[st][0]=='i'&&A[st].y==2); if (k<B[st].y){ if (P[st+1]>=1&&P[st+1]<=n){ //printf("%d %d %d\n",st+1,index[P[st+1]],index[Q[st+1]]); newi=pos[P[st+1]];assert(newi); if (F[i][k]>F[newi][k]) F[newi][k]=F[i][k],prei[newi][k]=i,prek[newi][k]=k, walk[newi][k]=-1,extra[newi][k]=0; }else if (F[i][k]>F[tot+1][k]) F[tot+1][k]=F[i][k],prei[tot+1][k]=i,prek[tot+1][k]=k, walk[tot+1][k]=-1,extra[tot+1][k]=0; continue; }st+=2;assert(str[st][0]=='s'); if (Q[st]<1||Q[st]>n){ if (F[i][k]>F[tot+1][k]) F[tot+1][k]=F[i][k],prei[tot+1][k]=i,prek[tot+1][k]=k, walk[tot+1][k]=2,extra[tot+1][k]=0; }else{ assert(pos[Q[st]]); newi=pos[Q[st]]; if (F[i][k]>F[newi][k]) F[newi][k]=F[i][k],prei[newi][k]=i,prek[newi][k]=k, walk[newi][k]=2,extra[newi][k]=0; } ++st;assert(A[st].y==2&&B[st].x==0); //fprintf(stderr,"%d %d\n",sign[st],B[st].y); newk=k+sign[st]*B[st].y; assert(newk<=k); //fprintf(stderr,"%d\n",F[i][k]+best[i]); if (F[i][k]+best[i]>F[i+1][newk]) F[i+1][newk]=F[i][k]+best[i],prei[i+1][newk]=i,prek[i+1][newk]=k, walk[i+1][newk]=1,extra[i+1][newk]=i; } //fprintf(stderr,"%d %d\n",F[tot][500],F[tot+1][500]); for (k=0;k<M;k++) if (F[tot+1][k]>ans) ans=F[tot+1][k],tmpi=tot+1,tmpk=k; fprintf(stderr,"%d\n",ans); freopen("train10.out","w",stdout); BACK(tmpi,tmpk); return 0;}
- NOI2013 小Q的修炼 题解
- [题解]bzoj3240(NOI2013)矩阵游戏
- 小 Q 的棋盘
- 【NOI2013】树的计数
- BZOJ 3240 [Noi2013] 矩阵游戏 题解
- 【CQOI2017】小Q的棋盘
- CQOI2017 小Q的棋盘
- BZOJ3244[NOI2013树的计数]
- BZOJ3244: [Noi2013]树的计数
- bzoj3244: [Noi2013]树的计数
- 【BZOJ3244】【NOI2013】树的计数
- 吐血、、、关于小Q的穿越问题
- 小q的博客开通啦
- 【数论】[CQOI2017]小 Q 的表格
- bzoj 4813 [Cqoi2017]小Q的棋盘
- 【CQOI2017】bzoj4813 小Q的棋盘
- BZOJ 4813 [Cqoi2017]小Q的棋盘
- bzoj4815 [Cqoi2017]小Q的表格
- 使用ItemTouchHelper轻松实现RecyclerView拖拽排序和滑动删除
- 在目录中递归查找文件名的插件
- 5月书讯:阳光穿过银杏树
- ScrollView嵌套ViewPager内容显示不全的问题
- CodeForces 508A Pasha and Pixels
- NOI2013 小Q的修炼 题解
- Notepad++编写的shell脚本在linux下无法执行的解决方法
- Android二维码扫描登陆网页
- 切一张图片,如果图片太大,如何解决
- Android开发之Google地图开发
- 常用正则表达式大全 (转)
- QT历届版本下载总汇
- 位图
- 用pyenv和virtualenv搭建单机多版本python虚拟开发环境