题目链接:http://hihocoder.com/problemset/problem/1035
题目显然是一个树形DP,我们用dp[ i ][ j ]表示已经询问了子树i的所有关键节点,人车的一个状态。其中
j==0:人去,不管人是否回来
j==1:人去,人一定要回来
j==2:人车都去,人车都要回来
j==3:人车都去,人一定要回来,车不管
j==4:人车都去,不管人车最后是否回来。
注意,这里显然有 dp[i][1]>=dp[i][0] ,dp[i][1]>=dp[i][2]>=dp[i][3]>=dp[i][4]
我们先考虑比较容易计算的,我们记人走边(fa,son)的花费为w1(fa,son),车为w2(fa,son),显然
- dp[fa][1] = ∑ dp[son][1] + 2*w1(fa,son)
- dp[fa][2] = ∑ min( dp[son][1] +2* w1(fa,son) , dp[son][2] + 2*w2(fa,son) )
对dp[fa][0],其实就是有一个边选择为 dp[son][0] + w1(fa,son) 而其他的都选择 dp[son][1] + 2*w1(fa,son);或者都选后者,我们记
- temp = min(dp[son][0] - dp[son][1] - w1(fa,son) ),temp = min(temp,0)
- 则 dp[fa][0] = dp[fa][1] + temp;
dp[fa][3]的决策方式,其实就是一个选择 w2(fa,son) + dp[son][3] + w1(fa,son) ,其他的选t2,或者都选t2,这样我们得到dp[fa][3]的转移方程
- t3=0,t3=min( t3 , w1(fa,son)+ w2(fa,son)+ dp[son][3] - t2 );
- dp[fa][3] = dp[fa][2] + t3;
1、走最后一棵子树时,还有车
这样的情况还是很好计算的,最后一棵子树的选择是 min( w1(fa,son) + dp[son][0] , w2(fa,son) + dp[son][4] ) 而其他选择都是t2。或者全部选择t2。
2、走最后一棵子树时,没有车了。
这种情况大概就是,最后一棵子树的决策是w1(fa,son)+w2(fa,son)+dp[son][3];其他的,某一棵子树,人车走下去,然后只有人回来了,剩下的决策是t2。
这个情况是最麻烦的,因为有两棵特殊的子树,我们类似上面的差值统计的时候,要避免两个差值代表同一个子树。为了解决这个问题,我最开始的方法是,先后将两个差值的优先级设为不同,这样统计的话,可以避免重复子树。但是因为顺序处理没处理好,我的代码最开始有BUG。但是因为能A这题,所以我没注意到。
这里多谢swwlqw的指点,这个问题现在已经改正。
我们记
- ff1 = w1(fa,son)+ w2(fa,son)+ dp[son][3] - t2;
- ff2 = w1(fa,son)+ dp[son][0] - t2;
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef long long LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF=100011122;
const double INFF=1e100;
const double eps=1e-8;
const int mod=1000000007;
const int NN=1000010;
const int MM=1000010;
/* ****************** */
struct G
{
int v,w1,w2,next;
}E[NN*2];
int p[NN],T;
bool vis[NN];
LL dp[NN][5];
int si[NN];
void add(int u,int v,int w1,int w2)
{
E[T].v=v;
E[T].w1=w1;
E[T].w2=w2;
E[T].next=p[u];
p[u]=T++;
}
void dfs(int u,int fa)
{
int i,v;
si[u]=vis[u];
LL temp=0,t2,t3=0,t41=0;
LL f1,f2,ff1,ff2;
int match = -1;
f1=f2=1LL<<50;
dp[u][1]=0;
dp[u][2]=0;
dp[u][4]=0;
for(i=p[u];i+1;i=E[i].next)
{
v=E[i].v;
if(v==fa)continue;
dfs(v,u);
si[u]+=si[v];
if(si[v]>0)
{
temp=min(temp,dp[v][0]-dp[v][1]-E[i].w1);
dp[u][1]+=dp[v][1]+E[i].w1*2;
t2=min(dp[v][1]+E[i].w1*2,dp[v][2]+E[i].w2*2);
dp[u][2]+=t2;
t3=min(t3,E[i].w1+E[i].w2+dp[v][3]-t2);
dp[u][4]+=t2;
t41=min(t41, min(dp[v][0]+E[i].w1,dp[v][4]+E[i].w2) - t2 );
ff1=E[i].w1+E[i].w2+dp[v][3]-t2;
if(ff1<f1)
{
match = v;
f2 = f1;
f1 = ff1;
}
else
f2=min(f2,ff1);
}
}
dp[u][0]=dp[u][1]+temp;
dp[u][3]=dp[u][2]+t3;
dp[u][4]+=t41;
dp[u][4]=min(dp[u][4],dp[u][3]);
for(i=p[u];i+1;i=E[i].next)
{
v=E[i].v;
if(v==fa)continue;
if(si[v]>0)
{
t2=min(dp[v][1]+E[i].w1*2,dp[v][2]+E[i].w2*2);
ff2=E[i].w1+dp[v][0]-t2;
if(v==match)
dp[u][4] = min(dp[u][4], dp[u][2] + f2 + ff2);
else
dp[u][4] = min(dp[u][4], dp[u][2] + f1 + ff2);
}
}
}
int main()
{
int n,m,i,u,v,w1,w2;
while(scanf("%d",&n)!=EOF)
{
memset(p,-1,sizeof(p));
T=0;
memset(vis,false,sizeof(vis));
for(i=1;i<n;i++)
{
scanf("%d%d%d%d",&u,&v,&w1,&w2);
add(u,v,w1,w2);
add(v,u,w1,w2);
}
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%d",&u);
vis[u]=true;
}
dfs(1,-1);
cout<<min(dp[1][0],dp[1][4])<<endl;
}
return 0;
}