您的位置:首页 > 其它

Lightoj 1037 - Agent 47 详解(状压DP)

2017-01-19 23:12 246 查看
1037 - Agent 47



 
  

PDF (English)StatisticsForum
Time Limit: 2 second(s)Memory Limit: 32 MB
Agent 47 is in a dangerous Mission "Black Monster Defeat - 15". It is a secret mission and so 47 has a limited supply of weapons. As a matter of fact he has only one weapon the old weak "KM .45 Tactical (USP)". The mission
sounds simple - "he will encounter at most 15 Targets and he has to kill them all". The main difficulty is the weapon. After huge calculations, he found a way out. That is after defeating a target, he can use target's weapon to kill other targets. So there
must be an order of killing the targets so that the total number of weapon shots is minimized. As a personal programmer of Agent 47 you have to calculate the least number of shots that need to be fired to kill all the targets.



Agent 47
Now you are given a list indicating how much damage each weapon does to each target per shot, and you know how much health each target has. When a target's health is reduced to 0 or less, he is killed. 47 start off only
with the KM .45 Tactical (USP), which does damage 1 per shot to any target. The list is represented as a 2D matrix with the ith element containing N single digit
numbers ('0'-'9'), denoting the damage done to targets 0, 1, 2, ..., N-1 by the weapon obtained from target i, and the health is represented as a series of N integers, with the ith element
representing the amount of health that target has.

Given the list representing all the weapon damages, and the health each target has, you should find the least number of shots he needs to fire to kill all of the targets.

Input

Input starts with an integer T (≤ 40), denoting the number of test cases.

Each case begins with a blank line and an integer N (1 ≤ N ≤ 15). The next line contains N space separated integers between 1 and 106 denoting the health of the targets 0,
1, 2, ..., N-1
. Each of the next N lines contains N digits. The jth digit of the ith line denotes the damage done to target j, if you use the
weapon of target i in each shot.

Output

For each case of input you have to print the case number and the least number of shots that need to be fired to kill all of the targets.

Sample Input

Output for Sample Input

2

 

3

10 10 10

010

100

111

 

3

3 5 7

030

500

007

Case 1: 30

Case 2: 12

 

 

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

思路:还是状压DP,用二进制表示每个炮台的状态,如果是1说明被摧毁了,这个炮台被摧毁了,武器可以用了,然后从所有被摧毁的武器中,就在已经摧毁的武器里找一个可以用最少次数摧毁当前炮台的炮台,用一个for循环就行,如果不用状压 要开一个16维的数组记录。。

总结下,状压其实就是把状态变成二进制了,每一位的0,1表示这一位的状态,关键还是dp的状态转移,本质还是转移,只不过在转移中有时需要用到许多位运算。比如这个题,通过二进制枚举1到n位所有可能的状态组合,也就是1到(1<<n)-1,同事时还很好改变状态,如果访问了,就把这一位变成1,枚举了所有的状态,肯定可以找出最优解,这题区别一些dp题直接贪心思想

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int INF = 1e6 + 5;
const int maxn = 16;
int n;
int dp[1<<maxn], h[maxn], a[maxn][maxn];
char str[maxn][maxn];
int dfs(int s)
{
if(s == (1<<n)-1)
return dp[s] = 0;
if(dp[s] != INF)
return dp[s];
for(int i = 0; i < n; i++) //这n个炮台的状态,也是第一次选择
{
if(!(s & (1<<i))) //如果这个炮台没被用过
{
int t = INF;
for(int j = 0; j < n; j++) //从所有没用过的里面找费用最小的
{
if((s&(1<<j)) && a[j][i] != 0) //确保没用过
{
int k = h[i]/a[j][i] + (h[i]%a[j][i] ? 1 : 0);
t = min(t, k);
}
}
if(t == INF)
t = h[i];
dp[s] = min(dp[s], dfs(s | (1 << i)) + t); //dp决策,这里手动递归就懂了,这是在枚举所有情况,选择的顺序
// cout << dp[s] << endl;
}
}
return dp[s];
}
int main()
{
int t, ca = 1;
cin >> t;
while(t--)
{
cin >> n;
for(int i = 0; i <= (1<<n); i++)
dp[i] = INF;
for(int i = 0; i < n; i++)
cin >> h[i];
for(int i = 0; i < n; i++)
cin >> str[i];
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
a[i][j] = str[i][j] - '0';
printf("Case %d: %d\n", ca++, dfs(0));
}
return 0;
}

这个是网上for循环,直接用for循环枚举所有状态,上面是用递归,模拟选择过程
using namespace std;
#define Debug(x) cout << #x << " " << x <<endl
#define Memset(x, a) memset(x, a, sizeof(x))
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef pair<int, int> P;
#define FOR(i, a, b) for(int i = a;i < b; i++)
#define lson l, m, k<<1
#define rson m+1, r, k<<1|1
#define MAX_N 16
int t;
int n;
int target[MAX_N]; //目标i 的血量
int p[MAX_N][MAX_N]; //p[i][j]武器i 对 敌人j 的 伤害
int dp[1<<MAX_N];
void solve(){
Memset(dp, 0x3f);
dp[0] = 0;
for(int s = 0;s < (1<<n)-1; s++){
for(int i = 0;i < n; i++){
if(!(s&(1<<i))){
int t = INF; //把第i个人杀死需要多少次
for(int j = 0;j < n; j++){
if(s & (1<<j) && p[j][i] != 0){
int k; //用第j个武器杀第i个人需要多少次
int m = target[i] / p[j][i];
k = (target[i] % p[j][i]) ? m+1 : m;
t = min(t, k);
}
}
if(t == INF) t = target[i]; //如果不能用敌人的武器杀,只有用攻击力为1的武器杀
dp[s|(1<<i)] = min(dp[s|(1<<i)], dp[s] + t);
}
}
}
printf("%d\n", dp[(1<<n)-1]);
}
int main(){
freopen("in.cpp", "r", stdin);
cin.tie(0);
ios::sync_with_stdio(false);
scanf("%d", &t);
int cnt = 1;
while(t--){
scanf("%d", &n);
for(int i = 0;i < n; i++){
scanf("%d", &target[i]);
}
string s[MAX_N];
for(int i = 0;i < n; i++){
cin >> s[i];
}
for(int i = 0;i < n; i++){
for(int j = 0;j < n; j++){
p[i][j] = s[i][j]-'0';
Debug(p[i][j]);
}
}
printf("Case %d: ", cnt++);
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: