Problem I: Plants vs. Zombies HD Super Pro
Plants versus Zombies HD Super Pro is a game played not a grid, but on a connected graph G with no cycles (i.e., a tree). Zombies live on edges of the tree and chew through edges so that tree falls apart! Plants can be purchased and placed on vertices of the tree to protect the tree from falling apart. It is not possible to plant more than one plant on the same vertex. A plant protects one or more adjacent edges, depending on the strength and capabilities of the plant.
The Almanac offers you to buy any of three different types of plants:
- PEASHOOTERS: These are your first line of defense and can shoot peas along any one edge (of your choosing) adjacent to the vertex upon which it is placed. Cost: $100 per plant.
- SPLIT PEAS: These are hard working pea shooters and can shoot peas along any two edges (of your choosing) adjacent to the vertex upon which it is placed. Cost: $175 per plant.
- STARFRUIT: Having just visited the dentist, a STARFRUIT is very upset and shoots stars along all edges adjacent to the vertex upon which it is placed. Cost: $500 per plant.
Your goal is to protect the tree from the Zombies by having every edge covered by at least one plant, and doing so spending the least amount of money. You can buy more than one of each type of plant, but you can only plant at most one plant on each vertex.
Input Format
The input starts with an integer T - the number of test cases (T <= 100). T cases follow, each starting with the integer N on the first line, the number of vertices in G (2 <= N <= 10,000). N-1 line follows, each containing two space separated integers u and v (0 <= u,v <= N-1, u ≠ v) - describing an edge.
Output Format
For each test case, print on a separate line the minimum cost of protecting the tree, formatted like in the sample output.
Sample Input
2 2 0 1 3 0 1 1 2
Sample Output
$100 $175
In the second case we can put a Split Pea on the vertex 1.
题意: 给一颗n个点的树,每个点上只能放一种植物,共有三种植物,第一种可以覆盖与种植点相邻的一条边,费用是100,第二种是可以覆盖两条边,费用是175,第三种是可以覆盖所有边,费用500。求最小花费的金额,使得树上每一条边都能被覆盖。
思路:果断树DP。设dp[u][0]表示以u为根节点且u不覆盖u到他父亲节点的边的最小费用,dp[u][1]表示以u为根节点且u覆盖u到他父亲节点的边的最小费用。
然后分四种情况考虑状态转移。以下用v表示u的子节点
1.u不种植物:dp[u][0] = dp[v][1],而且不种植物是不可能连接父节点,即无dp[u][1]
2.u种第一种: dp[u][0] = dp[v][0] (选一个子节点) + dp[v][1] (剩余的所有子节点之和) + cost[1]
dp[u][1] = dp[v][1] (所有子节点之和) + cost[1]
3.u种第二种: dp[u][0] = dp[v][0](选两个子节点) + dp[v][1] (剩余所有子节点) + cost[2]
dp[u][1] = dp[v][0] (选一个子节点) + dp[v][1] (剩余所有子节点) + cost[2]
4.u种第三种: dp[u][0] 是不可能的
dp[u][1] = dp[v][0/1](所有子节点) + cost[3]
至于选择哪一个子节点,这里要用贪心,选一个差值最大的来搞,记录下sum1和sum0,以及最大差值dmx,次大差值dmx2
注意下使用第三种植物的时候不能简单用sum1和sum0,因为有dp[v][1] < dp[u][0]的情况存在,今天比赛就是坑这个位置了,遗憾啊。。
代码
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int INF = 1e9; const int N = 10010; struct _edge{ int v,next; }; _edge edge[N*2]; int first[N],n,ecnt; int dp[N][2]; int cost[5]; inline void add(int u,int v) { edge[ecnt].v = v; edge[ecnt].next = first[u]; first[u] = ecnt++; } void dfs(int u,int fa) { dp[u][0] = dp[u][1] = INF; bool flag = 1; int sum1,sum0,dmx,dmx2; sum1=sum0=0; int sum3=0; dmx=dmx2=0; for(int e=first[u];e!=-1;e=edge[e].next) { int v = edge[e].v; if(v==fa) continue; flag = 0; // 1: dfs(v,u); sum1 += dp[v][1]; sum0 += dp[v][0]; sum3 += min(dp[v][1],dp[v][0]); int d = dp[v][1]-dp[v][0]; if(d>dmx2) { dmx2 = d; if(dmx2 > dmx) { swap(dmx,dmx2); } } } if(flag) { dp[u][0]=0; dp[u][1]=cost[1]; return; } dp[u][0] = min(sum1 - dmx + cost[1],sum1 - dmx - dmx2 + cost[2]); dp[u][0] = min(dp[u][0],sum1); dp[u][1] = min(min(sum1 + cost[1],sum1 - dmx + cost[2]),sum3 + cost[3]); } void run() { memset(first,-1,sizeof(first)); ecnt=0; scanf("%d",&n); int u,v; for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs(0,-1); // for(int i=0;i<n;i++) // { //// printf("%d: %d %d\n",i,dp[i][0],dp[i][1]); // } printf("$%d\n",min(dp[0][0],dp[0][1])); } int main() { //freopen("in","r",stdin); cost[1]=100;cost[2]=175;cost[3]=500; int _; scanf("%d",&_); while(_--) run(); return 0; }