NKOI 3504 迷宫

来源:互联网 发布:淘宝博库图书专营店 编辑:程序博客网 时间:2024/06/07 02:50

迷宫

Time Limit:10000MS  Memory Limit:165536K
Total Submit:2 Accepted:2 
Case Time Limit:1000MS

Description

迷宫里有 N(编号 0 到 N-1)个房间,任意两个房间之间有且仅有一条路径可以到达。每个房间里面都 有一个礼包,如果你进入了这个房间,你将得到这个礼包。当你离开一个房间后,你就不能再次进入这 个房间了。但是,有的房间里面有陷阱,当你取得礼物后,你就会被困住一会。你可以任意选择一个房 间作为起点,当你没有房间可去或者被困住了 C 次,游戏就结束了。每个礼包都有一定的价值,何老板 希望得到的礼包价值总和尽可能大,问这个最大值是多少?

Input

第一行,两个整数 N 和 C。 
接下来 N 行,每行两个横竖,表示该房间的礼物的价值和是否有陷阱,0 表示没有,1 表示有。 
接下来 N-1 行,每行两个整数 A 和 B,表示 A 和 B 间有道路直接相连。

Output

Sample Input

样例输入1:3 1 23 0 12 0 123 10 2 2 1样例输入:3 223 0 12 0 123 10 2 2 1

Sample Output

样例输出1:146样例输出2:158

Hint

2 <= N <= 50000 
1 <= C <=3

Source

2013 Multi-University Training Contest 2


显然:行走路径一定是一条链.分析如下

我们走到一个节点的时候,可以选这个节点,也可以不选,如果不选的话我们就不能选他的儿子节点

由于题目明确给出,每一个点都可以当作根节点,因此我们可以将当前讨论的一个点当作“中转点”,也就是说从他的子树讨论到这个点再去讨论连接这个点的其他路径,因此是一个链

于是我们可以以每个结点为根,讨论一遍这棵树。
dp[i][j]表示从i号节点出发,经过j个陷阱的一条链能获得的最大价值。
选出其中最大的一个dp[i][C]即可。此题数据弱,该方法可以通过。

但是如果数据稍微大一点,这种方法就不行了。

因此我们应该考虑如何避免以每个节点为根,进行这棵树进行N次讨论

我们任选一个节点,作为根,讨论整棵树。

dp[i][j]表示在以i为根的子树中,从任意节点出发,经过j个陷阱,走到i,所得的最大价值。

题目规定,经过C次陷阱,游戏马上结束。现在问题来了,如何处理这个结束条件。 若j==C,那么起点或者终点i两者间必须至少有一个陷阱(否则的话根本不会停止)。 若j!=C,那么起点和终点i都可以没有陷阱。
于是,我们还要在状态上加一维,来标记起点是否有陷阱。

dp[i][j][1]表示在以i为根的子树中,从任意有陷阱的节点出发,经过j个陷阱,走到i,所得的最大价值。

dp[i][j][0]表示在以i为根的子树中,从任意无陷阱的节点出发,经过j个陷阱,走到i,所得的最大价值。

状态转移:

1.当i点本身就有陷阱时:dp[i][j+1][ ]=max{dp[s][j][ ]+val[i]},0<=j<C,s是i的儿子;

2.当i点本身没有陷阱时:dp[i][j][ ]=max{dp[s][j][ ]+val[i]},0<=j<C,s是i的儿子;

3.特殊情况:当j==C时,因为路上经过了C个陷阱,所以起点和终点不能同时没有陷阱,所以只有dp[i][C][1],能用上式转移,dp[i][C][0]是不可能的。

我们讨论i为根的子树时,可以把i想象成两条链连接的交汇点。 一条链上经过了j个陷阱,另一条链经过了k个陷阱。j+k<=C

Ans=max{
                      dp[i][j][1]+dp[s][k][1]        j+k<=C,  s为i的儿子
                      dp[i][j][0]+dp[s][k][0]        j+k<c    
                      dp[i][j][0]+dp[s][k][1]        j<C
                      dp[i][j][1]+dp[s][k][0]        k<C
               }

#include<iostream>#include<cstdio>using namespace std;const int inf=5e8;const int maxn=100005;int n,c,f[maxn][4][2],val[maxn],ans=-inf;int trap[maxn],END[maxn],NEXT[maxn],LAST[maxn],cnt;//trap数组记录i点是否有陷阱inline void _read(int &x){    char t=getchar();bool sign=true;    while(t<'0'||t>'9')    {if(t=='-')sign=false;t=getchar();}    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';    if(!sign)x=-x;}void insert(int a,int b){END[++cnt]=b;NEXT[cnt]=LAST[a];LAST[a]=cnt;}void DP(int u,int fa){f[u][trap[u]][trap[u]]=val[u];ans=max(ans,f[u][trap[u]][trap[u]]);for(int i=LAST[u];i;i=NEXT[i]){//讨论u的子节点int s=END[i];if(s==fa)continue;//如果讨论到u的父节点,跳过DP(s,u);for(int j=0;j<=c;j++)    for(int k=0;k+j<=c;k++){    if(j+k<=c)ans=max(ans,f[u][j][1]+f[s][k][1]);    if(j+k<c)ans=max(ans,f[u][j][0]+f[s][k][0]);    if(k<c)ans=max(ans,f[u][j][1]+f[s][k][0]);    if(j<c)ans=max(ans,f[u][j][0]+f[s][k][1]);        }for(int j=0;j<c;j++){                f[u][j+trap[u]][0]=max(f[u][j+trap[u]][0],f[s][j][0]+val[u]);                f[u][j+trap[u]][1]=max(f[u][j+trap[u]][1],f[s][j][1]+val[u]);          }          if(!trap[u])f[u][c][1]=max(f[u][c][1],f[s][c][1]+val[u]);//判断特殊情况}}int main(){_read(n);_read(c);int i,j,k,x,y;for(i=1;i<=n;i++){_read(val[i]);_read(trap[i]);}for(i=1;i<n;i++){_read(x);_read(y);insert(x+1,y+1);insert(y+1,x+1);}for(i=1;i<=n;i++)    for(j=0;j<=c;j++)        for(k=0;k<2;k++)            f[i][j][k]=-inf;DP(1,-1);//为了防止后续讨论时0与其他节点的讨论重名,将1的父亲赋成-1cout<<ans;}

0 0
原创粉丝点击