poj 3345 Bribing FIPA
http://poj.org/problem?id=3345
题意,一个人想用钱贿赂一些国家获得选票,贿赂不同的国家需要不同数量金钱,有些国家可控制另一些国家,也就是贿赂了这些国家,被他们控制国家的票也会投给这个人,无环,每个点最多有一个父节点,求得n张选票的最少花费
输入真心恶心啊这个题……
之后就是树形dp了,用了两次dfs,第一次求出每个节点及其以下共有多少个,第二次求出最少花费,两个可以合在一起写的,只是这样感觉更容易想写
dp[p][j] = min(dp[p][j], dp[p][k] + dp[v[p][i]][j-k]);
dp[p][j]表示,在p及p的子树中, 选j个国家,最少花费
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
using namespace std;
const int maxn = 210;
const int INF = 0x3f3f3f3f;
int value, m, n, w[maxn], vis[maxn], dp[210][210], ans[maxn];
vector <int> v[maxn];
map<string, int> mp;
int count(int p){
int i, j, k;
ans[p] = 1;
for( i= 0; i< v[p].size(); i++){
ans[p] += count(v[p][i]);
}
return ans[p];
}
int dfs(int p){
int i, j, k;
for( i= 1; i<= ans[p]; i++)
dp[p][i] = w[p];
dp[p][0] = 0;
for( i= 0; i<v[p].size(); i++){
dfs(v[p][i]);
for( j= min(n, ans[p]); j>= 0; j--){
for( k= 0; k<= j; k++){
dp[p][j] = min(dp[p][j], dp[p][k] + dp[v[p][i]][j-k]);
}
}
}
}
int main(){
freopen("1.txt", "r", stdin);
int i, j, k, tt, val;
char tmp[110], tmp1[110], tmp2[110], ch;
while( scanf("%s", tmp) && tmp[0] != '#' ){
sscanf(tmp, "%d", &m);
scanf("%d", &n);
for( i= 0; i<= 200; i++) v[i].clear();
mp.clear();
memset( vis, 0, sizeof( vis));
memset( ans, 0, sizeof( ans));
memset( dp, 0x3f, sizeof( dp));
for( i= 0, tt= 1; i< m; i++){
scanf("%s%d", tmp, &val);
if ( mp[tmp] == 0) mp[tmp] = tt++;
w[mp[tmp]] = val;
while( 1){
while( scanf("%c", &ch)&& ch == ' ');
if( ch == '\n' || ch == '#') break;
else{
tmp1[0] = ch;
j= 1;
while( scanf("%c", &ch) && (ch!= ' ' && ch!= '\n' && ch!= '#'))
tmp1[j++] = ch;
tmp1[j] = '\0';
if( mp[tmp1] == 0) mp[tmp1] = tt++;
v[mp[tmp]].push_back(mp[tmp1]);
vis[mp[tmp1]] = 1;
}
if( ch == '\n') break;
}
}
for( i= 1; i<= m; i++)
if( !vis[i]) v[0].push_back(i);
w[0] = INF;
// cout<<mp["a"]<<mp["b"]<<mp["c"]<<mp["d"]<<mp["e"]<<mp["f"]<<mp["g"]<<mp["h"]<<mp["i"]<<endl;
count(0);
dfs(0);
printf("%d\n", dp[0][n]);
if( ch == '#') break;
}
return 0;
}