您的位置:首页 > 其它

POJ 3345 Bribing FIPA 树形dp

2012-04-01 13:57 267 查看
http://poj.org/problem?id=3345

题意 :给定N个国家,相互之间可能存在附属关系,现在想要贿赂m个国家,已知,贿赂一个国家,那么如果该国家拥有附属国,那么他的所有附属国都可以算作已经贿赂。

思路:树形dp。dp[u][j]:表示在以u为根的子树中选出j个国家的最小花费。这里在求以u为根的子树中选出j个国家的花费的时候还是用dfs 的方法,先求出孩子结点的值,然后从叶子结点递归上去,进行一次背包,求出一u为根的子树中找出j个国家的最小费用。还有很多的细节需要注意,具体见代码注释。
#include <cstdio>
#include <cstring>
#define MIN(a,b) (a)>(b)?(b):(a)
const int INF = 0x3f3f3f3f ;
char ch[210] ;
int N ,M ;
char name[210][110],na[110];
bool map[210][210] ;
int V[210], d[210];
int vote[210] ;
int dp[210][210] ;
int cnt ;

int find(char *p){
for(int i=1;i<cnt;i++){
if(strcmp(p , name[i]) == 0)	return i ;
}
strcpy(name[cnt] , p);
cnt ++ ;
return cnt - 1 ;
}

void Input(){
int i , j ,k ,f,len,u,a ,v;
sscanf(ch,"%d%d",&N,&M);
memset(map , 0 ,sizeof(map) );
memset( d , 0 , sizeof(d) );
cnt = 1 ;
for(i=1;i<=N;i++){
scanf("%s%d",ch,&a);
u = find(ch)  ;
V[u] = a ;
while(getchar() != '\n'){
scanf("%s",ch);
v = find(ch) ;
map[u][v] = 1 ;
d[v] ++ ;
}
}
}
void dfs(int u){
int res = 1 ;
for(int i=1;i<=N;i++){
if(map[u][i] == 1){
dfs(i);
res += vote[i] ;
}
}
vote[u] = res ;
}
void Build(int u){			//建树
for(int i=1;i<=N;i++){
if(d[i])	continue ;
map[u][i] = 1 ;
}
V[0] = INF ;
dfs(0);	vote[0] = N + 1;
}
int DP(int u){
dp[u][0] = 0 ;		//只有根结点一个结点,此时先假定根结点不选。
int nn = 0 ;		//当前根结点总共可以选择的国家数。
for(int v=1;v<=N;v++){
if(map[u][v] == 0)	continue ;
nn += DP(v) ;
for(int j=nn;j>=0;j--){
for(int k=1;k<=j;k++){
if(dp[u][j-k]<INF && dp[v][k]<INF && dp[u][j]>dp[u][j-k]+dp[v][k]){
dp[u][j] = dp[u][j-k] + dp[v][k] ;
}
}
}
}
for(int i=1;i<=vote[u];i++){			//题目的关键:若选择了根结点,则相应的孩子全选。此处更新必须。
dp[u][i] = MIN( dp[u][i] , V[u]);
}
return nn + 1; 			//返回以u为根的子树的国家的数目
}
int main(){
while(true){
gets(ch);
if(ch[0] == '#')	break ;
Input() ;
Build(0);
memset(dp,0x3f ,sizeof(dp) );
DP(0);
printf("%d\n",dp[0][M]);
}
return 0 ;
}


代码:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: