题目:hdu 4003 Find Metal Mineral
题意:火星上发现了一些n个矿厂,有 k 个机器人从 s 点出发采矿,给出路段间的花费cost,求最小的花费采所有的矿。
分类:树形dp + 分组背包
分析:结论1:假如我们从 i点出发k个机器人采完以 k 为根节点的所有矿又回到 i 点,那么花费为 i 为根节点的cost 的和 乘以 k。
对于每个节点,定义状态:dp【i】【j】 用 j 个机器人去采以 i 为根节点的子树的所有矿石的最小花费
状态转移方程:dp【father】【num】 = min(dp【father】【num】,dp【father】【num-k】+dp【child.to】【k】+k*child.cap);
其中num为father节点枚举机器人数量,k为child节点枚举机器人数量,num-k为father节点除了k个给child给其他孩子节点的机器人。
注意:在转移过程中的初始化、可以通过结论1来优化。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <queue> #include <vector> #include <map> #include <set> #include <string> using namespace std; #define Del(a,b) memset(a,b,sizeof(a)) const int N = 10100; struct Node { int to; int cap; }; vector<Node> v[N]; int dp[N][12],vis[N]; ///dp[i][0] 为从下往上最短距离 dp[i][1] 为从下往上此短距离 dp[i][2] 为从经过root最短距离 int n,s,k; void creat(int father) { vis[father]=1; for(int i=0;i<v[father].size();i++) { Node child=v[father][i]; if(vis[child.to]==1) continue; creat(child.to); for(int num=k;num>=0;num--) //注意0 { dp[father][num]+=dp[child.to][0]+2*child.cap;//最大结果 for(int j=1;j<=num;j++) dp[father][num]=min(dp[father][num],dp[father][num-j]+dp[child.to][j]+j*child.cap);//派j个机器人给v子树就会经过father-child之间的边j次 } } } int main() { while(~scanf("%d%d%d",&n,&s,&k)) { for(int i=0;i<n-1;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); Node tmp; tmp.to=y,tmp.cap=z; v[x].push_back(tmp); tmp.to=x; v[y].push_back(tmp); } Del(vis,0);Del(dp,0); creat(s); printf("%d\n",dp[s][k]); for(int i=1;i<=n;i++) v[i].clear(); } return 0; }