题目大概说一棵n个结点的树,每个结点都可以安装某一规格的一个塔,塔有价格和能量两个属性。现在一个敌人从1点出发但不知道他会怎么走,如果他经过一个结点的塔那他就会被塔攻击失去塔能量的HP,如果HP小于等于0敌人就挂了。任务就是在总花费不超过m的条件下在各个结点安装塔,求能预防的敌人的HP的最大值。
状态容易表示,dp[u][m]表示在结点u为根的子树中花费m能预防的最大的HP
转移显然又是树上背包了,不过略麻烦,想清楚的话还是能比较快地写完:
- u子树从它孩子结点的子树的最小值中转移过来,因为各个孩子都必须选我用了一个临时数组存值转移完后更新回去
- 这样处理完u的各个子树,再用一遍背包加上u结点本身能建的塔,就是dp[u]状态的值了
WA了一发,因为同一价格不同能量没考虑到。。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define MAXN 1111 6 struct Edge{ 7 int v,next; 8 }edge[MAXN<<1]; 9 int NE,head[MAXN]; 10 void addEdge(int u,int v){ 11 edge[NE].v=v; edge[NE].next=head[u]; 12 head[u]=NE++; 13 } 14 int m,mat[MAXN][222]; 15 int d[MAXN][222],tmp[222]; 16 void dp(int u,int fa){ 17 d[u][0]=0; 18 bool first=1; 19 for(int i=head[u]; i!=-1; i=edge[i].next){ 20 int v=edge[i].v; 21 if(v==fa) continue; 22 dp(v,u); 23 if(first){ 24 first=0; 25 for(int j=0; j<=m; ++j) d[u][j]=d[v][j]; 26 continue; 27 } 28 memset(tmp,-1,sizeof(tmp)); 29 for(int j=0; j<=m; ++j){ 30 for(int k=0; j+k<=m; ++k){ 31 tmp[j+k]=max(tmp[j+k],min(d[u][j],d[v][k])); 32 } 33 } 34 for(int j=0; j<=m; ++j) d[u][j]=tmp[j]; 35 } 36 for(int i=m; i>=0; --i){ 37 for(int j=0; j<=i; ++j){ 38 if(d[u][i-j]==-1 || mat[u][j]==-1) continue; 39 d[u][i]=max(d[u][i],d[u][i-j]+mat[u][j]); 40 } 41 } 42 } 43 int main(){ 44 int t,n,a,b,c; 45 scanf("%d",&t); 46 while(t--){ 47 NE=0; 48 memset(head,-1,sizeof(head)); 49 scanf("%d",&n); 50 for(int i=1; i<n; ++i){ 51 scanf("%d%d",&a,&b); 52 addEdge(a,b); addEdge(b,a); 53 } 54 memset(mat,-1,sizeof(mat)); 55 scanf("%d",&m); 56 for(int i=1; i<=n; ++i){ 57 scanf("%d",&a); 58 while(a--){ 59 scanf("%d%d",&b,&c); 60 mat[i][b]=max(mat[i][b],c); 61 } 62 } 63 memset(d,-1,sizeof(d)); 64 dp(1,1); 65 int res=0; 66 for(int i=0; i<=m; ++i) res=max(res,d[1][i]); 67 printf("%d ",res); 68 } 69 return 0; 70 }