POJ 3345——Bribing FIPA(树形DP)

题目分析:

现在有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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值