poj 3345 树形dp(收买国家)

来源:互联网 发布:参与网络博客是否违法 编辑:程序博客网 时间:2024/05/16 19:27

题目大意:一共有n(<=200)个国家, 现在目标是要收买m(<=n)个国家。其中收买国家i的代价是v[ i ]。但是有些国家有拥属关系,而且这些拥属关系构成一棵有根树。如果A拥属B国,那么买通了A也意味着买通了以A为根的所有子树。求收买至少m个国家的最少代价。

思路:树形dp。首先需要考虑输入可能是森林,那么加入一个虚拟点,连接到所有树的根节点从而形成一整棵树。其次就是dp过程了,比较好想,就相当于做了个背包。

#include <cstdio>#include <string>#include <cstdlib>#include <cmath>#include <algorithm>#include <map>using namespace std;#define INF 0x3fffffff#define clr(s,t) memset(s,t,sizeof(s))#define N 205map<string,int> mm;int n,m;struct edge{    int y,next;}e[N<<1];int dp[N][N],s[N],first[N],top,son[N];char t[105];void add(int x,int y){    e[top].y = y;    e[top].next = first[x];    first[x] = top++;}void dfs(int x){    int i,j,k,y;    son[x] = 0;    for(i = first[x];i!=-1;i=e[i].next){        y = e[i].y;        dfs(y);        for(j = son[x];j>=0;j--)                for(k = son[y];k>=1;k--)                    dp[x][j+k] = min(dp[x][j+k],dp[x][j]+dp[y][k]);        son[x] += son[y];    }    if(x){        son[x]++;        dp[x][son[x]] = s[x];    }    for(i = son[x]-1;i>=1;i--)        dp[x][i] = min(dp[x][i],dp[x][i+1]);}int main(){    int i,j,now,len;    char ch;    while((ch = getchar())&&ch!='#'){        ungetc(ch, stdin);        scanf("%d %d",&n,&m);        mm.clear();        clr(first, -1);        clr(son, 0);        top = len = 0;        for(i = 0;i<=n;i++)            for(j = 0;j<=n;j++)                dp[i][j] = (j?INF:0);        for(i = 1;i<=n;i++){            scanf("%s",t);            if(!mm.count(t))                mm[t] = (++len);            now = mm[t];            scanf("%d",&s[now]);//先求出now才能输入,而不是输入到mm[i]中,wa了多次            while((ch=getchar()) && ch!='\n'){                scanf("%s",t);                if(!mm.count(t))                    mm[t] = (++len);                add(now,mm[t]);                son[mm[t]] = 1;            }        }        for(i = 1;i<=n;i++)            if(!son[i])                add(0,i);        dfs(0);        printf("%d\n",dp[0][m]);    }}


0 0
原创粉丝点击