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;}

1 0