NOI2013 小Q的修炼 题解
Posted 阿蒋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOI2013 小Q的修炼 题解相关的知识,希望对你有一定的参考价值。
感觉这种题答都没有题解,而我又想在这里保存一下代码,就写一份题解咯。
【题意】
一共有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_pair
using 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_pair
using 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_pair
using 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)constreturn 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_pair
using 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_pair
using 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的修炼 题解的主要内容,如果未能解决你的问题,请参考以下文章
noi 2.7_413Calling Extraterrestrial Intelligence Again(算法效率)