Game
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 1899 Accepted Submission(s): 604
For each testcase, the first line contains one integer N(2 <= N <= 50000), the number rooms, and another integer C(1 <= C <= 3), the number of chances to be trapped. Each of the next N lines contains two integers, which are the value of gift in the room and whether have trap in this rooom. Rooms are numbered from 0 to N-1. Each of the next N-1 lines contains two integer A and B(0 <= A,B <= N-1), representing that room A and room B is connected.
All gifts' value are bigger than 0.
树形dp,也可以直接dfs,不过这题数据太bug。。
暴力:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
typedef long long ll;
const int N = 50010,INF=999999999;
vector<int> tu[N];
int val[N];
int flag[N];
bool vis[N];
int n,c;
int dfs(int x,int pre,int cc)
{
if(cc-flag[x]<=0)return val[x];
else cc-=flag[x];
int l=tu[x].size(),ans=val[x],max0=0;
for(int i=0; i<l; i++)
{
int t=tu[x][i];
if(t==pre)continue;
max0=max(max0,dfs(t,x,cc));
}
return ans+max0;
}
int main()
{
int t;
cin>>t;
while(t--)
{
scanf("%d%d",&n,&c);
for(int i=0; i<n; i++)
tu[i].clear();
memset(flag,0,sizeof(flag));
for(int i=0; i<n; i++)
scanf("%d%d",&val[i],&flag[i]);
for(int i=0; i<n-1; i++)
{
int a,b;
scanf("%d%d",&a,&b);
tu[a].push_back(b);
tu[b].push_back(a);
}
int ans=-1;
for(int i=0; i<n; i++)
if(tu[i].size()==1)
ans=max(ans,dfs(i,-1,c));
printf("%d\n",ans);
}
}
用dp[u][j]表示以u为根的子树上,从某点走到u且走过j个陷阱能得到的最大值;
考虑如果最优解走过了k个陷阱,如果k!=C,那么其起点和终点都可以为没有陷阱的点,如果k=C,那么起点或者终点至少有一点为有陷阱的点,所以dp数组还需要增加一维:dp[u][i][flag],若flag=1,表示以u为根的子树上,从一个有陷阱的点走到u走过j个陷阱能得到的最大值,若flag=0,表示起点没有陷阱。
状态转移:
1) 当u点本身就有陷阱时,dp[u][j+1][flag]=max{dp[v][j][flag]+val[u]},0<=j<C,v是u的儿子;
2) 当u点没有陷阱时,dp[u][j][flag]=max{dp[v][j][flag]+val[u]},0<=j<C,特殊的,当j=C时,因为路上走过了C个陷阱,所以起点和终点不能同时为没有陷阱的点,所以只有flag=1,能用上式转移,也就是说dp[u][C][0]是不可能的情况。
这两种情况可以合成一种写。
更新ans:最优解可以看成两条链拼在一起,枚举两条链上陷阱的个数然后再拼起来。
树形dp:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
typedef long long ll;
#define maxn 50005
const long long INF=999999999;
int n,c;
vector<int> tu[maxn];
int val[maxn],flag[maxn];
int ans,dp[maxn][4][2];
void dfs(int u,int p)
{
int s=tu[u].size();
dp[u][flag[u]][flag[u]]=val[u];
ans=max(ans,dp[u][flag[u]][flag[u]]);
for (int i=0; i<s; i++)
{
int v=tu[u][i];
if (v==p) continue;
dfs(v,u);
for (int j=0; j<=c; j++)
for (int k=0; k+j<=c; k++)
{
if (k+j<=c) ans=max(ans,dp[u][j][1]+dp[v][k][1]);
if (k+j<c) ans=max(ans,dp[u][j][0]+dp[v][k][0]);
if (j!=c) ans=max(ans,dp[u][j][0]+dp[v][k][1]);
if (k!=c) ans=max(ans,dp[u][j][1]+dp[v][k][0]);
}
for (int j=0; j<c; j++)
{
dp[u][j+flag[u]][0]=max(dp[u][j+flag[u]][0],dp[v][j][0]+val[u]);
dp[u][j+flag[u]][1]=max(dp[u][j+flag[u]][1],dp[v][j][1]+val[u]);
}
if (!flag[u]) dp[u][c][1]=max(dp[u][c][1],dp[v][c][1]+val[u]);
}
}
int main()
{
int t,a,b;
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&c);
for (int i=0; i<n; i++)
tu[i].clear();
for (int i=0; i<n; i++)
scanf("%d%d",&val[i],&flag[i]);
for (int i=1; i<n; i++)
{
scanf("%d%d",&a,&b);
tu[a].push_back(b);
tu[b].push_back(a);
}
ans=0;
for (int i=0; i<n; i++)
for (int j=0; j<4; j++)
for (int k=0; k<2; k++)
dp[i][j][k]=-INF;
dfs(0,-1);
printf("%d\n",ans);
}
return 0;
}