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; }