【树形dp】hdu 2415 Bribing FIPA

来源:互联网 发布:百度云计算阳泉中心 编辑:程序博客网 时间:2024/05/16 08:21

hdu 2415 Bribing FIPA

题目:给定由若干个树组成的森林, 树上的边是有向边, 树上的每个节点都有一个代价. 若要得到某个节点, 需要付出该节点对应的代价, 若该节点拥有后继, 那么后继的节点也都能获得. 求解使用最少的代价取得至少 m 个节点
这道题输入是个问题,之前用 getchar()!='\n' 调了一晚上始终过不了,最后用C++的stringstream过了,还以为是dfs的问题,输入是巨坑!!!

说一下思路1)建树的时候会遇到孤立结点,设想一个虚拟节点0,没有前驱的节点都作为虚拟根节点的后继,从而建立一个森林,这一步比较容易想到;
(2)dp[i][j]表示第i个国家及其附属国中选j个国家的最小花费;
状态转移方程为:dp[u][j+k] = min(dp[u][j], dp[u][j-k]+dp[v][k]) 需要理解!
(3)dp[u][j]的初始化问题:对于子树u使得 j~1:tot[u] 的所有dp[u][j]=v[u] ,其中tot[u]代表子树u的所有节点个数,v[u]表示节点u的费用;由于无法事先确定tot[u],只能边搜索边返回,然后取min:dp[u][j]=min{dp[u][j],v[u]},最后使用带返回值的dfs(int u)__返回子树u的所有节点数目
  最后,vector<>建树,map<>给国家编号,树形dp求解
sscanf的使用
使用stringstream对象简化类型转换
详细理解见注释:
/*Author:Hacker_vision*/#include<bits/stdc++.h>#define clr(k,v) memset(k,v,sizeof(k))#define INF 0x3f3f3f3fusing namespace std;const int _max=3e2+10;int n,m,v[_max];//有n个国家及其附属关系,要从中选择m个国家得到m张投票;v[i]表示i的花费char str1[110],str2[110];int dp[310][310];//dp[u][j]=min(dp[u][j],dp[u->child][j-k]+dp[u->child][k])bool vis[310];//判断是否有孤立结点,是的话加到虚节点vector<int>child[_max];//vector建树map<string,int>mp;//mp映射,给国家名编号map<int,string>name;int dfs(int u){  int size=child[u].size();  int tot=1; //存储子树i包含的所有结点数目,包括树根  dp[u][0]=0;  dp[u][1]=v[u];  for(int i = 0; i < size; ++ i ){    tot+=dfs(child[u][i]); //递归返回部分,记录子树u包含的节点数,用作初始化    for(int j = n;j >= 0; -- j ){       for( int k = 0; k <= j; ++ k){        dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[child[u][i]][k]);//子节点选j-k,其他的选k个,完成选择      }                                                    //尝试理解所有节点遍历完的情况一定是最优解    } //注意:不一定选m个国家正合适,可能超过m反而更小,从n计数  }   for(int i=1; i<= tot;i++)    dp[u][i]=min(dp[u][i],v[u]); //这部分本来应该作初始化的,但是不知道子树u的结点数,  return tot;                    //只能边递归边初始化:若放在之前的话dp[u][i]=v[u];}int  main(){ // freopen("input.txt","r",stdin);  char str[1000];  while( gets(str) && str[0] != '#') {    sscanf(str,"%d%d", &n, &m);    int top=1,cost;    clr(vis,0);    for(int i=0; i<=n; ++i ) child[i].clear();    mp.clear();//使用vector\map容器先清空    for(int i=1; i<=n; ++i ){       scanf("%s%d",str1,&cost);       if(!mp[str1]) mp[str1]=top++;       v[mp[str1]]=cost;//cost赋值给对应编号的费用v[]中         name[i]=str1;       gets(str);       stringstream ss(str);  //ss读入str       while( ss >> str2 ) {  //ss读出到str2         if(!mp[str2]) mp[str2]=top++;         child[mp[str1]].push_back(mp[str2]);         vis[mp[str2]]=true;       }    }    v[0]=INF;    for( int i =1;i <=n; i++ )       if(vis[i]==false) child[0].push_back(i);//孤立节点i加入虚拟根节点,构建森林     for( int i =0;i <=n; i++ )        for( int j = 0; j<= n ; ++ j )           dp[i][j]=INF; //因为要取最小值,dp[][]初始化最大即可     dfs(0);//从虚拟节点深搜    printf("%d\n",*min_element(dp[0]+m,dp[0]+n+1));//找到选择m~n个国家中的最小值  }  return 0;}


1 0
原创粉丝点击