「NOI2013」小 Q 的修炼 解题报告
「NOI2013」小 Q 的修炼
第一次完整的做出一个提答,花了半个晚上+一个上午+半个下午
总体来说太慢了
对于此题,我认为的难点是观察数据并猜测性质和读入操作
我隔一会就思考这个sb字符串读起来怎么这么麻烦啊
首先可以发现,这个所有的选择都之后往后走,就是个topo图
-
task1,2,3
观察到数据有形如
s x x+11 v 3 + c y v 4 + c y v 5 + c y v 6 + c y v 7 + c y v 8 + c y v 9 + c y v 10 + c y v 11 + c y v 12 + c y
之类的东西,而且每个这样的循环后面有一个这样的东西
i v 3 c 0 43 45 v 1 - v 3 i c 0 c 1 46 0 v 1 + v 3 v 3 - v 3 i v 4 c 0 48 50 v 1 - v 4 i c 0 c 1 51 0 v 1 + v 4 v 4 - v 4 ...
发现这个就是把正的数据加到1上,负的不变并清空
这样就没法贪心或者dp了
然后我们又发现循环长度很小,直接爆搜就行了
代码搞丢了..
S_1 要讨论走哪个循环
S_2 直接爆搜
S_3 发现又很多组,分组爆搜
-
task4,5,6
最开始对变量2有一个正的初值
然后
s 5 8 v 2 - c 10 v 1 + c 22750 i c 0 c 1 8 0 i v 2 c 9 9 10 i c 0 c 1 14 0
循环节长这样
每次如果选择,会有一些跳转之类的
发现2的初值比较小而且后面只会减
考虑dp
\(dp_{i,j}\)第\(i\)行第\(2\)个变量的值是\(j\)的时候最大的\(1\)
然后模拟转移,记录路径就可以了
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long using std::max; const int N=6010; int n,m; char op[5],t0[10],t1[10]; int x0,x1,x2,x2; struct koito_yuu { int op,a,b,c; koito_yuu(){} koito_yuu(int Op,int A,int B,int C){op=Op,a=A,b=B,c=C;} }yuu[N]; struct node { int i,j,cho; node(){} node(int I,int J,int Cho){i=I,j=J,cho=Cho;} }pre[N][5010]; ll dp[N][5010]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",op); if(op[0]=='s') { tim[++k]=i; scanf("%d%d",&x1,&x2); yuu[i]=koito_yuu(1,x1,x2,0); } else if(op[0]=='v') { scanf("%d%s%s%d",&x0,t0,t1,&x1); yuu[i]=koito_yuu(2,x0,t1[0]=='+'?x1:-x1,0); } else { scanf("%s%d%s%d%d%d",t0,&x0,t1,&x1,&x2,&x3); if(t0[0]=='c'&&t1[0]=='c') yuu[i]=koito_yuu(3,x2,0,0); else if(t0[0]=='v'&&t1[0]=='c') yuu[i]=koito_yuu(4,x1,x2,x3); } } memset(dp,-0x3f,sizeof dp); int inf=dp[0][0]; dp[0][0]=0; for(int i=1;i<=n;i++) for(int j=0;j<=5000;j++) if(dp[i-1][j]!=inf) { if(yuu[i].op==1) { int a=yuu[i].a,b=yuu[i].b; if(dp[a][j]<dp[i][j]) { pre[a][j]=node(i,j,1); dp[a][j]=dp[i][j]; } if(dp[b][j]<dp[i][j]) { pre[b][j]=node(i,j,2); dp[b][j]=dp[i][j]; } } else if(yuu[i].op==2) { int a=yuu[i].a,b=yuu[i].b; if(a==1) { if(dp[i+1][j]<dp[i][j]+b) { pre[i+1][j]=node(i,j,0); dp[i+1][j]=dp[i][j]+b; } } else { int t=max(0,j+b); if(dp[i+1][t]<dp[i][j]) { pre[i+1][t]=node(i,j,0); dp[i+1][t]=dp[i][j]; } } } else if(yuu[i].op==3) { int a=yuu[i].a; if(dp[a][j]<dp[i][j]) { pre[a][j]=node(i,j,0); dp[a][j]=dp[i][j]; } } else { int a=yuu[i].a,b=yuu[i].b,c=yuu[i].c; if(j<a) { if(dp[b][j]<dp[i][j]) { pre[b][j]=node(i,j,0); dp[b][j]=dp[i][j]; } } else { if(dp[c][j]<dp[i][j]) { pre[c][j]=node(i,j,0); dp[c][j]=dp[i][j]; } } } } int tj=0; for(int i=1;i<=5000;i++) if(dp[n+1][tj]<dp[n+1][i]) tj=i; int ti=n+1,cnt=0; while(ti) { int tx=ti,ty=tj; ti=pre[tx][ty].i,tj=pre[tx][ty].j; if(pre[tx][ty].cho) ans[++cnt]=pre[tx][ty].cho; } for(int i=cnt;i;i--) printf("%d\n",ans[i]); return 0; }
4,5,6都是一样的
我本地跑6的时候居然开不下空间,还压了一会T_T
-
task7,8,9,10
这四个点就是前面的结合
最开始的时候有2控制跳转的东西
中间就是1,2,3点的循环
差不多175行一个周期
对周期压变量2的长度dp,每个周期里面爆搜
说起来简单,写起来还是很麻烦的
我这个代码要把in的最后几行删掉
Code:
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long using std::max; using std::min; const int N=35010; int n,m; char op[5],t0[10],t1[10]; int x0,x1,x2,x3; struct koito_yuu { int op,a,b,c; koito_yuu(){} koito_yuu(int Op,int A,int B,int C){op=Op,a=A,b=B,c=C;} }yuu[N]; struct node { int i,j,cho; node(){} node(int I,int J,int Cho){i=I,j=J,cho=Cho;} }pre[N][1010]; ll dp[N][1010]; int Id[N],as[N][20],yuy[20][20],ct[N]; ll solve(int id,int L,int R) { int n=0; for(int i=L;i<=R;i++) { if(yuu[i].op==1) ++n,yuy[n][0]=0; if(yuu[i].op==2) yuy[n][++yuy[n][0]]=yuu[i].b; if(yuu[i].op==3) break; } ll mx=-(1ll<<52); ct[id]=n; for(int s=0;s<1<<n;s++) { ll sum[20]={}; for(int i=1;i<=n;i++) if(s>>i-1&1) { for(int j=1;j<=10;j++) sum[j]+=yuy[i][j]; } ll su=0; for(int i=1;i<=10;i++) su+=sum[i]>0?sum[i]:-sum[i]; if(mx<su) { mx=su; for(int i=1;i<=n;i++) if(s>>i-1&1) as[id][i]=1; else as[id][i]=2; } } return mx; } int endro[N],k,ans[N]; int main() { freopen("train8.in","r",stdin); freopen("train8.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",op); if(op[0]=='s') { scanf("%d%d",&x1,&x2); x2=min(x2,n+1); yuu[i]=koito_yuu(1,x1,x2,0); } else if(op[0]=='v') { scanf("%d%s%s%d",&x0,t0,t1,&x1); if(t1[0]=='c') { yuu[i]=koito_yuu(2,x0,t0[0]=='+'?x1:-x1,0); } else { yuu[i]=koito_yuu(3,x0,0,x1); if(x0==12) endro[++k]=i; } } else { scanf("%s%d%s%d%d%d",t0,&x0,t1,&x1,&x2,&x3); x2=min(x2,n+1),x3=min(x3,n+1); if(t0[0]=='c'&&t1[0]=='c') yuu[i]=koito_yuu(4,x2,0,0); else if(t0[0]=='v'&&t1[0]=='c') yuu[i]=koito_yuu(5,x1,x2,x3); } } memset(dp,-0x3f,sizeof dp); dp[1][0]=0; for(int i=1;i<=k;i++) { int tp; for(int j=endro[i-1]+1;;j++) if(yuu[j].op==1&&yuu[j].b-yuu[j].a==11) { tp=j-1; break; } ll ad=solve(i,tp+1,endro[i]); for(int l=endro[i-1]+1;l<=tp;l++) for(int j=0;j<=1000;j++) { if(yuu[l].op==1) { int a=yuu[l].a,b=yuu[l].b; if(dp[a][j]<dp[l][j]) { dp[a][j]=dp[l][j]; pre[a][j]=node(l,j,1); } if(dp[b][j]<dp[l][j]) { dp[b][j]=dp[l][j]; pre[b][j]=node(l,j,2); } } else if(yuu[l].op==2) { int b=yuu[l].b; int t=max(0,j+b); if(dp[l+1][t]<dp[l][j]) { dp[l+1][t]=dp[l][j]; pre[l+1][t]=node(l,j,0); } } else if(yuu[l].op==4) { int a=yuu[l].a; if(dp[a][j]<dp[l][j]) { dp[a][j]=dp[l][j]; pre[a][j]=node(l,j,0); } } else { int a=yuu[l].a,b=yuu[l].b,c=yuu[l].c; if(j<a) { if(dp[b][j]<dp[l][j]) { dp[b][j]=dp[l][j]; pre[b][j]=node(l,j,0); } } else { if(dp[c][j]<dp[l][j]) { dp[c][j]=dp[l][j]; pre[c][j]=node(l,j,0); } } } } int s=tp+1,t=endro[i]+1; Id[s]=i; for(int j=0;j<=1000;j++) if(dp[t][j]<dp[s][j]+ad) { dp[t][j]=dp[s][j]+ad; pre[t][j]=node(s,j,3); } } //9.22925584 //10.20218188 int tj=0; for(int i=1;i<=1000;i++) if(dp[n+1][tj]<dp[n+1][i]) tj=i; int ti=n+1,cnt=0; while(ti) { int tx=ti,ty=tj; ti=pre[tx][ty].i,tj=pre[tx][ty].j; if(pre[tx][ty].cho) { if(pre[tx][ty].cho==3) { int id=Id[ti]; for(int i=ct[id];i;i--) ans[++cnt]=as[id][i]; } else ans[++cnt]=pre[tx][ty].cho; } } for(int i=cnt;i;i--) printf("%d\n",ans[i]); return 0; }
2019.4.12