题意:我和你玩游戏,刚开始有初始分数m,有n次操作,每次操作只能选a,b,c(为0不能选)三种之一,a不为0时,可以选择加a分,b不为0时,可以选择减b分,c不为0时,可以选择把当前分数变成相反数,我先选,你后选,一人一次来,我想让最后分数>=k,你想让最后分数<=l,n次操作结束后,分数大于等于 k 输出good,小于等于 l 输出bad,中间就输出nomal。
思路:由初始分数m计算出双方最优策略操作后最后的分数,太难,但是我们可以倒着想,在双方最优策略下,看看初始分数m可以由最后哪个分数得出来,这样就简单多了,设d[ i ][ j ]表示第 i 次操作时,当前分数为 j ,在双方最优策略后最后分数为d[ i ][ j ],可以知道,d[ n+1 ][ j ]肯定等于 j,那么怎么倒推求d[ i ][ j ]?当a,b,c都不为0时,如果 i 为奇数,代表我来选择,我肯定是想最后分数越来越高,那么d[ i ][ j ]=max(d[ i ][ j+a ],d[ i ][ j - b ],d[ i ][ -j ]),表示我有这三种选择,哪种选择最后的分越高,我就选哪个,i 为偶数,就取最小,由于数组下标不能为负数,以及这题上下届为-100到100,因此这题要把区间换成0到200,注意倒推的时候,加法别超过200,减法别小于0。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1005;
int a[maxn],b[maxn],c[maxn],d[maxn][205];
int main()
{
int n,m,k,l,t;
while(~scanf("%d%d%d%d",&n,&m,&k,&l))
{
k+=100,l+=100,m+=100;//-100到100的区间换成0到200
for(int i=1;i<=n;i++)
scanf("%d%d%d",&a[i],&b[i],&c[i]);
for(int i=0;i<=200;i++)d[n+1][i]=i;
for(int i=n;i;i--)
for(int j=0;j<=200;j++)
{
int res1=0,res2=200;
if(a[i])
{
t=min(200,j+a[i]);
res1=max(res1,d[i+1][t]);
res2=min(res2,d[i+1][t]);
}
if(b[i])
{
t=max(0,j-b[i]);
res1=max(res1,d[i+1][t]);
res2=min(res2,d[i+1][t]);
}
if(c[i])
{
t=200-j;
res1=max(res1,d[i+1][t]);
res2=min(res2,d[i+1][t]);
}
if(i&1) d[i][j]=res1;
else d[i][j]=res2;
}
if(d[1][m]>=k)puts("Good Ending");
else if(d[1][m]<=l)puts("Bad Ending");
else puts("Normal Ending");
}
}