poj3345——bribing FIPA

这篇博客介绍了一种解决投票贿赂问题的方法,利用树形动态规划找到最少的钻石数量来确保美丽城镇赢得票选。通过构建森林模型,定义状态转移方程并进行深度优先搜索,求解最优策略。
摘要由CSDN通过智能技术生成

题目大意:美丽城镇想用钻石贿赂参与投票的国家给它投票,当贿赂一个国家时,还包含了这个国家管辖的国家,问最少用多少钻石能让美丽城镇赢得票选

输入:参与投票国家数量n 美丽城镇赢得票选所需票数m(1<=n<=200,0<=m<=n)

           国家名字 贿赂它所需的钻石数量 它管辖的国家名字

           #//代表结束输入

输出:最少钻石数量

分析:树形动态规划

           由于输入可能是个森林,所以要设置一个总根

           num[u]是以u为根的子树有多少个结点

           状态:dp[u][j]为以u为根的子树(国家)中选取j个结点(国家)的最小代价

           状态转移方程:dp[u][j]=min{dp[u][j],dp[v][k]+dp[u][j-k]},(k<=j && k<=num[v])

代码:转载自http://blog.csdn.net/ascii991/article/details/7448072(有改动,添加了一些注释)

  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <string>  
  4. #include <vector>  
  5. #include <map>  
  6. using namespace std;  
  7.   
  8. const int maxn=205,oo=99999999;  
  9. vector<int> g[maxn];  
  10. int dp[maxn][maxn],v[maxn],num[maxn];  
  11. bool fa[maxn];  
  12. int n,m,id;  
  13.   
  14. int dfs(int x)  
  15. {  
  16.     num[x]=1;  //记录以x为根的子树共有多少个结点
  17.     int i,j,k,y;  
  18.     for (k=0;k<g[x].size();k++)  
  19.     {  
  20.         y=g[x][k];  
  21.         num[x]+=dfs(y);  
  22.     }  
  23.     for (k=0;k<=n;k++) dp[x][k]=INF;  
  24.     dp[x][0]=0;  
  25.     dp[x][num[x]]=v[x];  
  26.     for (k=0;k<g[x].size();k++)  
  27.     {  
  28.         y=g[x][k];  
  29.         for (i=num[x];i>=0;i--)  //对于x为根来说,可以选择它子树中的num[x]~0个结点
  30.             for (j=0;j<=i && j<=num[y];j++)//减少以x为根选择的结点,增加以y为根选择的结点 ,看是否能让代价减小 
  31.                 if (dp[x][i-j]+dp[y][j]<dp[x][i])  
  32.                    dp[x][i]=dp[x][i-j]+dp[y][j];  
  33.     }  
  34.     return num[x];  
  35. }  
  36. int main()  
  37. {  
  38.     freopen("pin.txt","r",stdin);  
  39.     freopen("pou.txt","w",stdout);  
  40.     char str[1000];  
  41.     int i,j,ans,now;  
  42.     while (gets(str))  
  43.     {  
  44.           if (str[0]=='#'break;  
  45.           sscanf(str,"%d%d",&n,&m);  
  46.           map<string,int> wmap;  
  47.           for (i=0;i<=n;i++)  
  48.               g[i].clear();  
  49.           memset(fa,0,sizeof(fa));  
  50.           id=0;  
  51.           for (i=1;i<=n;i++)  
  52.           {  
  53.               scanf("%s",str);  
  54.               if (wmap.find(str)==wmap.end()) //如果str不在map中  
  55.                  wmap[str]=++id;  
  56.               now=wmap[str];  //当前国家的id编号
  57.               scanf("%d",&v[now]);  //选了now国家的代价
  58.               while (getchar()!='\n')  
  59.               {  
  60.                     scanf("%s",str);  
  61.                     if (wmap.find(str)==wmap.end())  
  62.                        wmap[str]=++id;  
  63.                     g[now].push_back(wmap[str]);  //push受now国家管辖的国家id编号
  64.                     fa[wmap[str]]=true;  //表明该国家是被管辖的,即有父亲结点
  65.               }  
  66.           }  
  67.           v[0]=INF;  //输入的可能是个森林,所以要建立一个总根
  68.           for (i=1;i<=n;i++)  
  69.           {  
  70.               if (fa[i]) continue;  
  71.               g[0].push_back(i);  将那些没有父亲结点的国家与总根相连
  72.           }  
  73.           dfs(0);  
  74.           ans=dp[0][m];  
  75.           for (i=m;i<=n;i++)  //要选举成功,必须有m或m以上各国家投它
  76.               if (dp[0][i]<ans) ans=dp[0][i];  
  77.           printf("%d\n",ans);  
  78.     }  
  79.     return 0;  
  80. }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值