树形DP,f(i,j)表示i节点及其子树得到j个国家所需的最小贿赂,对于j==i子树的节点总数sum,所需的最小费用肯定就是i节点本身的cost,对于j<sum的情况,肯定只有在其子树中选,如果选了i就是把整个子树都选了,然后,这个过程用的是01背包的思想,不知道这个词用的 恰不恰当,但想清楚,这个DP过程就可以了,对每个i的子树,恩。
//思路来啦 #include <iostream> #include <cstdio> #include <cstring> #define mem(a,b) memset(a,b,sizeof(a)) #define min(a,b) ((a)<(b)?(a):(b)) using namespace std; const int maxn=200+30; int inf=0x3f3f3f3f; char Map[maxn][110]; int f[maxn][maxn],g[maxn][maxn],head[maxn],e[maxn],next[maxn],vis1[maxn],vis2[maxn]; int cost[maxn],qn[maxn]; int n,m,tot,qe; int getL(char *s) { int i; for(i=0;i<tot;i++) if(strcmp(s,Map[i])==0) return i; strcpy(Map[tot++],s); return tot-1; } void dp(int u) { int i,j,k,v,p; if(!vis2[u]) { f[u][0]=0; f[u][1]=cost[u]; qn[u]=1; return; } qn[u]=0; f[u][0]=0; for(i=head[u];i!=-1;i=next[i]) { dp(e[i]); qn[u]+=qn[e[i]]; } qn[u]++; mem(g,inf); g[0][0]=0; int sum=0; for(i=head[u],j=1;i!=-1;i=next[i],j++)//01背包的思路 { v=e[i]; sum+=qn[v]; for(k=0;k<=sum;k++) { for(p=0;p<=qn[v];p++) { g[j][k]=min(g[j-1][k-p]+f[v][p],g[j][k]); } } } for(i=1;i<=sum;i++) f[u][i]=g[j-1][i]; f[u][qn[u]]=cost[u]; } int main() { char s[101]; while(gets(s)) { if(s[0]=='#') break; sscanf(s,"%d %d",&n,&m); tot=0;qe=0; int i,loc,am,j,u,v; mem(f,inf); mem(head,-1); mem(vis1,0);mem(vis2,0); for(i=0;i<n;i++) { scanf("%s %d",s,&am); u=getL(s); cost[u]=am; while(scanf("%c",s)&&s[0]!='\n') { vis2[u]=1; scanf("%s",s); v=getL(s); vis1[v]=1; next[qe]=head[u]; head[u]=qe; e[qe++]=v; } } for(i=0;i<n;i++) { if(!vis1[i]) { next[qe]=head[n]; head[n]=qe; e[qe++]=i; } } vis2[n]=1; cost[n]=inf; dp(n); int ans=inf; for(i=m;i<=(qn[n]-1);i++) ans=min(ans,f[n][i]); printf("%d\n",ans); } return 0; }