Lightoj 1037 Agent 47(状压DP)

来源:互联网 发布:淘宝店铺招牌代码 编辑:程序博客网 时间:2024/05/28 11:28

题意:你现在需要消灭n个敌人,n个敌人的血量已知,你的普通攻击力为1,但是如果你杀死敌人i可以用它的武器去杀死其他敌人,p[i][j] 表示用敌人i的武器射杀敌人j会减p[i][j]滴血.问你最少可以攻击多少次可以将敌人杀死。


思路:点击打开链接

定义集合s为死亡敌人的集合。

dp[s] 为让集合为s的人死亡最小需要的攻击次数。

如果现在想要消灭敌人i 并且敌人i不属于s

那么dp[s | (1<<i)] = min(dp[s|(1<<i)] , dp[s] + t);

t 为用死亡敌人的武器消灭敌人i的最小费用。如何求t成为了本题的关键。

考虑死亡敌人集合s,你同样也拥有了死亡敌人j的武器【j属于集合s】

那么t =  min(t, 用敌人j 的武器射杀i的次数) 【j属于集合s】

当t一次都没有被更新时,说明死亡敌人的武器没有可以消灭敌人i的,只能用普通武器攻击


代码:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int maxn = 16;const int INF = 0x3f3f3f3f;int dp[1<<maxn], tag[maxn], p[maxn][maxn];char ss[maxn];int main(void){    int t, ca = 1, n;    cin >> t;    while(t--)    {        scanf("%d", &n);        for(int i = 0; i < n; i++)            scanf("%d", &tag[i]);        for(int i = 0; i < n; i++)        {            scanf(" %s", ss);            for(int j = 0; j < n; j++)                p[i][j] = ss[j]-'0';        }        memset(dp, INF, sizeof(dp));        dp[0] = 0;        for(int i = 0; i < (1<<n); i++)        {            for(int j = 0; j < n; j++)            {                if(i&(1<<j)) continue;                dp[i|(1<<j)] = min(dp[i|(1<<j)], dp[i]+tag[j]);                for(int k = 0; k < n; k++)                {                    if(i&(1<<k))                    {                        int tm = 1;                        if(p[k][j]) tm = p[k][j];                        int t = tag[j]/tm;                        if(tag[j]%tm) t++;                        dp[i|(1<<j)] = min(dp[i|(1<<j)], dp[i]+t);                    }                }            }        }        printf("Case %d: %d\n", ca++, dp[(1<<n)-1]);    }    return 0;}


原创粉丝点击