题目分析:
现在有n个村子,你想要用收买m个国家为你投票,其中收买第i个国家的代价是val[i]。但是有些国家存在从属关系,如果B从属于A国,则收买了A也意味着买通了B,而且这些关系是传递的。问你最小要付出的代价是多少?
这题的难点在于怎么建图,弱菜不会,只能膜拜大神的代码,然后自己捉摸着敲,dfs部分就和一般的树形DP+背包差不多,只是状态的初始化有些变化
建图需要加个超级根,连接大国(小国和大国是从属关系)
dp [ i ] [ j ]表示以 i 为根取得 j 票所需要的最小费用
大牛用了map,今天看了下map的基础知识,涨姿势啦
贴上辛辛苦苦AC的代码,(我喜欢A,喜欢后面加个C或者加个V,但是不喜欢前面加个W)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<map>
#include<vector>
#include<set>
#include<algorithm>
#define inf 0x3f3f3f3f
#define M 210
#define ms(s,i) memset(s,i,sizeof(s))
using namespace std;
typedef map<string,int> msi;
struct pp{int v,w,next;}edge[M*2];int head[M],tot,root,n,m;
inline void addedge(int u,int v,int w,int *h){edge[tot].v=v,edge[tot].w=w,edge[tot].next=h[u],h[u]=tot++;}
int dp[210][210],vis[210],w[210],fa[210],num[210];
msi mp;
void dfs(int u)
{
vis[u]=1;
dp[u][0]=0;
num[u]=1; //统计父亲节点有多少个儿子
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(vis[v]) continue;
dfs(v);
num[u]+=num[v];
for(int k=num[u];k>=1;--k){ //儿子能取得的票数
for(int j=0;j<=k&&j<=num[v];++j){ //在票数里挑选最优
dp[u][k]=min(dp[u][k],dp[u][k-j]+dp[v][j]); //01背包
}
}
}
dp[u][num[u]]=w[u]; //
return;
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
char s[1000];
while(gets(s)){
if(s[0]=='#') break;
sscanf(s,"%d %d",&n,&m);
tot=0,mp.clear(),ms(dp,0x3f),ms(vis,0),ms(fa,0),ms(head,-1),ms(num,0);
int id=0;
for(int i=1;i<=n;++i){
scanf("%s",s);
if(mp.find(s)==mp.end()){
mp[s]=++id;
}
int u=mp[s];
scanf("%d",&w[u]);
while(getchar()!='\n'){
scanf("%s",s);
if(mp.find(s)==mp.end()){
mp[s]=++id;
}
addedge(u,mp[s],0,head);
fa[mp[s]]=1;
}
}
for(int i=1;i<=n;++i){
if(!fa[i]) addedge(0,i,0,head);
}
w[0]=0;
dfs(0);
int ans=inf;
for(int i=m;i<=n;++i){
if(dp[0][i]<ans) ans=dp[0][i];
}
cout<<ans<<endl;
}
return 0;
}