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