标签:树形dp
题目:
给定由若干个树组成的森林, 树上的边是有向边, 树上的每个节点都有一个代价. 若要得到某个节点, 需要付出该节点对应的代价, 若该节点拥有后继, 那么后继的节点也都能获得. 求解使用最少的代价取得至少 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 0x3f3f3f3f using 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; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:树形dp
原文地址:http://blog.csdn.net/u012717411/article/details/47021457