Light OJ 1037 - Agent 47(状压DP)

题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1037

题目大意:初始的时候你有一把手枪, 这把手枪每次只能造成1点伤害。现在有n个人和他们对应的血量, 每个人有一把武器。随后一个n*n的矩阵, 对于这个矩阵attack, attack[i][j]代表第i个人的武器可以对第j个人每次造成attack[i][j]点伤害。一旦杀死一个人,你就可以拥有他的武器。现在问,最小需要多少次,才能杀死所有人?

解题思路:由于n的范围很小, 所以枚举所有状态, 建立dp数组, dp[i]代表杀死集合i中所有人所花费的最小次数,那么

if(((i >> j)&1) == 0)
    dp[i|(1<<j)] = min(dp[i|(1<<j)], dp[i] + t);

t为用每次伤害最高的武器,杀死j需要的次数。 对于t, 每个状态i更新一下就行了。

代码如下:

#include<bits/stdc++.h>
using namespace std;

int blood[19];
int mp[19][19], killed[19];
int dp[1<<17];

void solve(int cases)
{
    int n;
    scanf("%d", &n);
    for(int i=1; i<=n; ++ i)
        scanf("%d", &blood[i]);
    for(int i=1; i<=n; ++ i)
    {
        for(int j=1; j<=n; ++ j)
        {
            char ch;
            scanf(" %c", &ch);
            mp[i][j] = ch - '0';
        }
    }
    memset(dp, 0x3f, sizeof(dp));
    dp[0] = 0;
    for(int i=0; i<(1<<n); ++ i)
    {
        for(int j=1; j<=n; ++ j)
            killed[j] = 1;

        int t = i, res = 0;
        while((t >> res))
        {
            if((t >> res) & 1)
            {
                for(int j=1; j<=n; ++ j)
                    killed[j] = max(killed[j], mp[res+1][j]);
            }
            res ++;
        }

        for(int j=0; j < n; ++ j)
        {
            if(((i >> j)&1) == 0)
            {
                int t = blood[j+1]/killed[j+1];
                if(blood[j+1]%killed[j+1] != 0)
                    t += 1;
                dp[i|(1<<j)] = min(dp[i|(1<<j)], dp[i] + t);
            }
        }
    }
    printf("Case %d: %d\n", cases, dp[(1<<n)-1]);
}

int main()
{
    int t;
    scanf("%d", &t);
    for(int i=1; i<=t; ++ i)
        solve(i);
    return 0;
}

 

posted @ 2017-02-26 11:28  aiterator  阅读(146)  评论(0编辑  收藏  举报