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的修炼 题解的主要内容,如果未能解决你的问题,请参考以下文章

NOI2013小Q的修炼

BZOJ2434: [Noi2011]阿狸的打字机

@noi.ac - 490@ game

noi 2.7_413Calling Extraterrestrial Intelligence Again(算法效率)

[蒟蒻修炼计划][bzoj3670][Noi2014]动物园

BZOJ3671[Noi2014]随机数生成器 暴力