[bzoj-4832][Lydsy2017年4月月赛]抵制克苏恩 题解

题目传送门
题意解析:题目告诉了我们攻击次数k,和a,b,c三种状态的奴隶主分别的个数,c可以变成b,b可以变成a,a可以直接消失,然后每次当c变成b或b变成a的时候,只要三种总数不到7,那么c的个数就会多出一。当然攻击可以打在人物角色本身。


My opinion:这题暴力贼好写,dfs(k,a,b,c,p,s)表示还剩k次攻击,三种状态的人还分别剩下a,b,c个,概率为p,人物被攻击了s次。转移的时候只要判断能不能多出现一个c状态的奴隶主,这样的话,最后只需要改成记忆化搜索,把p和s都消掉就好了(话说有一点我很想吐槽,不是说人物血量只有30吗,那么是不可能被攻击50次的,我居然认为这是一个隐藏条件来判断,WA了好多次)。这样写看起来时间复杂度为O(7*7*7*k*T),事实上因为(a+b+c)<=7所以没这么多状态,快很多。
总结:
我相信玩过炉石的人也许跳了人物血量只有30的坑。
1、输入。
2、记忆化搜索(因为这是double数组,所以不能设-1,但是有期望为0的状态,所以我们需要一个flag数组来记录这个状态是否出现过)。
3、输出(每组数组其实也许不需要每次清空dp数组)。


代码:

#include
#include
#include
#include
#include
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=a;i>=n;i--)
#define Clear(a,x) memset(a,x,sizeof(a))
#define ll long long
#define db double
#define INF 2000000000
#define eps 1e-8
using namespace std;
int read(){
    int x=0,f=1; 
    char ch=getchar();
    while (ch<'0'||ch>'9') f=ch=='-'?-1:f,ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int k,a,b,c;
db f[55][8][8][8];
bool flag[55][8][8][8];
db dfs(int k,int a,int b,int c){
    if (k==0) return 0;
    if (flag[k][a][b][c]) return f[k][a][b][c];
    db res=0;
    flag[k][a][b][c]=1;
    f[k][a][b][c]=0;
    res+=(dfs(k-1,a,b,c)+1)*(db)1/(a+b+c+1);
    int d=(a+b+c)<7?1:0;
    if (a>0) res+=dfs(k-1,a-1,b,c)*(db)a/(a+b+c+1);
    if (b>0) res+=dfs(k-1,a+1,b-1,c+d)*(db)b/(a+b+c+1);
    if (c>0) res+=dfs(k-1,a,b+1,c-1+d)*(db)c/(a+b+c+1);
    return f[k][a][b][c]=res;
}
int main(){
    int T=read();
    while (T--){
        Clear(flag,0);
        k=read(),a=read(),b=read(),c=read();
        printf("%.2f\n",dfs(k,a,b,c));
    }
    return 0;
}

你可能感兴趣的:(bzoj,dp)