[NOI2013小Q的修炼]提交答案(找规律)
又一次遇到提交答案题,,,orzorzorz,这种题总是要写好几个代码还不一定得分的那种,写得真是心态爆炸.
之前不小心把最后一份代码给搞没了,所以最后4个点口胡.orz
UOJ123
小 Q 最近发现了一款新游戏,游戏的目标是从一个新手修炼成为武功高强的大侠。
面对错综复杂的游戏世界,小 Q 要对他面临的每件事情做出谨慎的选择。例如,是否参加一个陌生人邀请的比武;同意或是拒绝用宝剑交换他人的武功秘籍……而小 Q 做出的每一个选择都有可能影响到他以后的发展:面对一个高手,若主动与之比武,很可能会损失惨重;但若不去比武,也许今后就再也见不到这个高手了。
对着这个游戏,小 Q 玩了很多次仍然玩不出他想要的结局,于是他费尽千辛万苦找到了游戏的剧本。令人惊讶的是,游戏的剧本并不像我们平时见到的剧本,反而很像代码。这个剧本是这样描述的:
量:有 $2$ 种量,常数和变量。
常数:一个整数。
变量:初始值为 $0$ 的可变整数,不同变量用不同正整数区分。
事件:整个剧本由若干个事件构成。所有的事件按照给定的顺序从 $1$ 开始依次编号。事件共有 $3$ 种:普通事件、选择跳转和条件跳转。
执行位置:一个整数,表示接下来将会执行的事件编号,如果不存在这个编号的事件则停止,即游戏到了一个结局。最初的时候执行位置为 $1$。
普通事件:一个变量增加或减少一个量的值。之后执行位置增加 $1$。
选择跳转:两个整数。执行到这里时玩家需要在这两个整数中选择一个,之后执行位置将被修改为这个整数。
条件跳转:两个量和两个整数。执行到这里时,若第一个量小于第二个量,则执行位置将被修改为第一个整数,否则将被修改为第二个整数。
小 Q 认为,整个游戏是希望一个叫做“成就值”的变量(编号为 $1$)最大。
ANS1 ANS2
直接爆搜模拟这个剧本就可以了.
考场上写得出来还保证不错个鬼
输入格式
所有输入数据 train1.in ~ train10.in 见数据下载。 输入的第一行包含两个正整数 $n,m$,表示事件的个数和变量的个数。 接下来有 $n$ 行,每行描述一个事件。这些事件按照给出的顺序依次编号为 $1$ 到 $n$。 描述量和事件的格式如下(“格式” 中 “#”表示空格)。类型 | 格式 | 例子 |
---|---|---|
常数 | c#整数 | c -2 |
变量 | v#正整数 | v 5 |
普通事件 | 变量#+#量 变量#-#量 | v 1 + c -1 v 2 - v 2 |
选择跳转 | s#整数1#整数2 | s 10 20 |
条件跳转 | i#量1#量2#整数1#整数2 | i c 99 v 2 0 1 |
输出格式
针对给定的 10 个输入文件 train1.in ~ train10.in,你需要分别提交你的输出文件 train1.out ~ train10.out。 每个文件需要输出若干行,每行输出一个字符“1”或“2”,表示执行过程中遇到的每个选择跳转所作的选择。 输出的行数需要严格等于此次游戏执行过程中遇到的选择跳转的个数。样例一
input
11 2 v 2 + c 19 i v 2 c 3 7 3 s 4 7 v 1 + c 13 v 2 - c 3 i c 0 c 1 2 0 i v 2 c 5 12 8 s 9 12 v 1 + c 23 v 2 - c 5 i c 0 c 1 7 0
output
1 1 1 2 1 1
explanation
根据样例输出的方案,执行位置进行了如下的变化: 1 → 2 → 3 → 4 → 5 → 6 → 2 → 3 → 4 → 5 → 6 → 2 → 3 → 4 → 5 → 6 → 2 → 3 → 7 → 8 → 9 → 10 → 11 → 7 → 8 → 9 → 10 → 11 → 7 → 12 当执行位置变成 $12$ 时,剧本结束。最终变量 $1$ 的值为 $85$。 事件 $1$ 为变量 $2$ 增加 $19$,可以认为是得到了 $19$ 单位的初始资金。 事件 $6$ 为无条件跳转到事件 $2$,可以看出这里是一个循环。从事件 $2$ 和事件 $3$ 可以看出,如果变量 $2$ 小于 $3$(资金不足一次购买)或者选择放弃则会跳出循环。 循环内的事件 $4$ 和事件 $5$ 为花费 $3$ 的资金得到 $13$ 的成就值。 事件 $7$ 到 $11$ 也是一个类似的循环,只是参数有所不同。为花费 $5$ 的资金得到 $23$ 的成就值。评分方式
对于每组数据,我们采用如下方式评分:如果你的输出不合法,得 $0$ 分。如果你的输出执行了超过 $10^6$ 行剧本,得 $0$ 分。如果你的输出能让剧本正常结束,得 $1$ 分。如果你的输出能让剧本正常结束,且结束时成就值为正数,得 $2$ 分。我们设置了 $8$ 个评分参数 $a_3, a_4, \dots, a_{10}$。如果你的输出能让剧本正常结束,且结束时成就值不小于 $a_s$,得 $s$ 分。如果以上条目有多项满足,则取满足条件中的最高得分。 形式化地讲,设在你的方案中,变量 $1$ 最终的值为 $v_1$。当你的输出合法时,你的分数将会由下表给出:得分 | 条件 | 得分 | 条件 |
---|---|---|---|
10 | $v_1 \geq a_{10}$ | 5 | $a_5 \leq v_1 < a_6$ |
9 | $a_9 \leq v_1 < a_{10}$ | 4 | $a_4 \leq v_1 < a_5$ |
8 | $a_8 \leq v_1 < a_9$ | 3 | $a_3 \leq v_1 < a_4$ |
7 | $a_7 \leq v_1 < a_8$ | 2 | $0 < v_1 < a_3$ |
6 | $a_6 \leq v_1 < a_7$ | 1 | $v_1 \leq 0$ |
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<ctime>
using namespace std;
int n,m,tim=1;
int V[15];
char ss[10],so[10];
struct date{
int opt;
char l1,l2;
int X,Y,to1,to2;
}z[10000];
int anssta[10000],anstop;
int sta[10000],ans,top;
void dfs(int x) {
if(x>n||x<1) {
if(V[1]>ans) {
ans = V[1];
anstop = top;
for(int i=1;i<=top;i++) anssta[i] = sta[i];
}
return;
}
if(z[x].opt==1) {
int oqo = 0;
if(z[x].l1=='+') {
if(z[x].l2=='c') oqo = z[x].Y;
else oqo = V[z[x].Y];
} else {
if(z[x].l2=='c') oqo = -z[x].Y;
else oqo = -V[z[x].Y];
}
V[z[x].X] += oqo;
dfs(x+1);
V[z[x].X] -= oqo;
} else if(z[x].opt==2) {
sta[++top] = 1;
dfs(z[x].X);
sta[top] = 2;
dfs(z[x].Y);
--top;
} else {
int A = z[x].X; int B = z[x].Y;
if(z[x].l1=='v') A = V[A];
if(z[x].l2=='v') B = V[B];
if(A<B) dfs(z[x].to1);
else dfs(z[x].to2);
}
}
int main() {
freopen("train2.in","r",stdin);
freopen("train2.out","w",stdout);
ans = -0x3f3f3f3f;
srand(time(NULL));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%s",&ss[0]);
if(ss[0]=='v') {
z[i].opt = 1;
scanf("%d%s%s%d",&z[i].X,&ss[0],&so[0],&z[i].Y);
z[i].l1 = ss[0]; z[i].l2 = so[0];
} else if(ss[0]=='s') {
z[i].opt = 2;
scanf("%d%d",&z[i].X,&z[i].Y);
} else {
scanf("%s%d%s%d%d%d",&ss[0],&z[i].X,&so[0],&z[i].Y,&z[i].to1,&z[i].to2);
z[i].l1 = ss[0]; z[i].l2 = so[0];
}
}
dfs(1);
for(int i=1;i<=anstop;i++) printf("%d\n",anssta[i]);
}
T3,我们发现他170行分成一块,每一块结束的时候会清空所有的变量,这意味着彼此之间独立,分开爆搜过去就可以了.
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<ctime>
using namespace std;
int n,m,tim=1;
int V[15];
char ss[10],so[10];
struct date{
int opt;
char l1,l2;
int X,Y,to1,to2;
}z[40000];
int anssta[40000],anstop;
int sta[40000],ans,top;
int ST;
void dfs(int x) {
if(x>ST*170||x<=(ST-1)*170) {
if(V[1]>ans) {
ans = V[1];
anstop = top;
for(int i=1;i<=top;i++) anssta[i] = sta[i];
}
return;
}
if(z[x].opt==1) {
int oqo = 0;
if(z[x].l1=='+') {
if(z[x].l2=='c') oqo = z[x].Y;
else oqo = V[z[x].Y];
} else {
if(z[x].l2=='c') oqo = -z[x].Y;
else oqo = -V[z[x].Y];
}
V[z[x].X] += oqo;
dfs(x+1);
V[z[x].X] -= oqo;
} else if(z[x].opt==2) {
sta[++top] = 1;
dfs(z[x].X);
sta[top] = 2;
dfs(z[x].Y);
--top;
} else {
int A = z[x].X; int B = z[x].Y;
if(z[x].l1=='v') A = V[A];
if(z[x].l2=='v') B = V[B];
if(A<B) dfs(z[x].to1);
else dfs(z[x].to2);
}
}
int main() {
freopen("train3.in","r",stdin);
freopen("train3.out","w",stdout);
ans = -0x3f3f3f3f;
srand(time(NULL));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%s",&ss[0]);
if(ss[0]=='v') {
z[i].opt = 1;
scanf("%d%s%s%d",&z[i].X,&ss[0],&so[0],&z[i].Y);
z[i].l1 = ss[0]; z[i].l2 = so[0];
} else if(ss[0]=='s') {
z[i].opt = 2;
scanf("%d%d",&z[i].X,&z[i].Y);
} else {
scanf("%s%d%s%d%d%d",&ss[0],&z[i].X,&so[0],&z[i].Y,&z[i].to1,&z[i].to2);
z[i].l1 = ss[0]; z[i].l2 = so[0];
}
}
for(int o=1;o<=200;o++) {
ans = -0x3f3f3f3f;
top = 0;
ST = o;
dfs((o-1)*170+1);
for(int i=1;i<=anstop;i++) printf("%d\n",anssta[i]);
}
}
T4,T5,T6
只有两个变量
我们发现这个是开始2获得一些值,然后不会往回跳(无后效性),然后选择进入就是1增加一些,2减少一些.我们发现这恰好体现出一了一格背包模型.造出背包跑就可以了.
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<vector>
using namespace std;
int n,m,tim=1;
int V[15];
char ss[10],so[10];
struct date{
int opt,id;
char l1,l2;
int X,Y,to1,to2;
}z[40005];
int tot,ans;
vector<int>ve[2005];
int c[40004],w[40004];
int f[2005][10005],g[2005][10005],pref[2005][10005],preg[2005][10005];
int st[40005];
int xz[2005];
void dfs(int x,int y,int o) {
if(x<=0) return;
if(o==1||y>=c[x]) xz[x] = o;
else xz[x] = 0;
if(o==1) {
if(pref[x][y]) dfs(pref[x][y],y+c[x],2);
else dfs(x-1,y+c[x],1);
} else {
if(preg[x][y]) dfs(preg[x][y],y,2);
else dfs(x-1,y,1);
}
}
int main() {
freopen("train4.in","r",stdin);
freopen("train4.out","w",stdout);
ans = -0x3f3f3f3f;
srand(time(NULL));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%s",&ss[0]);
if(ss[0]=='v') {
z[i].opt = 1;
scanf("%d%s%s%d",&z[i].X,&ss[0],&so[0],&z[i].Y);
z[i].l1 = ss[0]; z[i].l2 = so[0];
if(i==1) continue;
if(z[i].X==2) { c[++tot]=z[i].Y; }
else { w[tot]=z[i].Y; }
z[i].id = tot;
} else if(ss[0]=='s') {
z[i].opt = 2;
st[tot+1] = i;
z[i].id = tot+1;
scanf("%d%d",&z[i].X,&z[i].Y);
} else {
scanf("%s%d%s%d%d%d",&ss[0],&z[i].X,&so[0],&z[i].Y,&z[i].to1,&z[i].to2);
z[i].l1 = ss[0]; z[i].l2 = so[0];
z[i].id = tot+1;
}
}
z[n+1].id = tot+1;
for(int i=1;i<=tot;i++) {
ve[ z[z[st[i]].Y].id ].push_back(i);
}
memset(f,-0x3f,sizeof f);
f[0][5000] = 0;
int xz1 = 0 , xz2 = 0;
for(int i=1;i<=tot;i++) {
for(int j=c[i];j<=5000;j++) {
f[i][j-c[i]] = f[i-1][j] + w[i];
preg[i][j-c[i]] = 0;
for(int k=ve[i].size()-1;k>=0;k--) {
int y = ve[i][k];
if(g[y][j]+w[i]>f[i][j-c[i]]) {
f[i][j-c[i]] = g[y][j] + w[i];
pref[i][j-c[i]] = y;
}
if(f[i][j-c[i]]>ans) {
ans = f[i][j-c[i]];
xz1 = i; xz2 = j-c[i];
}
}
}
for(int j=0;j<=10000;j++) {
g[i][j] = f[i-1][j];
preg[i][j] = 0;
for(int k=ve[i].size()-1;k>=0;k--) {
int y = ve[i][k];
if(g[y][j]>g[i][j]) {
g[i][j] = g[y][j];
preg[i][j] = y;
}
}
}
}
dfs(xz1,xz2,1);
for(int i=1;i<=tot;i++) {
if(xz[i]) printf("%d\n",xz[i]);
}
}
T7,T8,T9,T10
和T4,T5,T6,基本上一样,只是这个的变量猛增到12个
观察了发现,似乎依然是消耗2赚1,并且一定块长,这些变量又要自我消除成0,发现花费2赚到的1实际上是和在块中的决策相关的.于是我们以花费2为分界,爆搜找出每个块的最大收益,完了之后又跑一个dp就可以了.
nocode orz