访问计划
题目描述
一棵n个点有边权的树,现在要从根节点出发遍历每条边并返回根节点,沿路行走的花费为边权,此外可以使用不超过k次传送门,每次花费c元跳到任意一个节点。求最小花费。
1<=n,k<=2000,多组数据,∑n<=10000,答案在int范围内。
本题包含多组数据。
每组数据第一行三个整数 N , K , C 。
接下来 N 行,每行三个整数 u , v , w 表示一条连接 u 和 v 的道路,通
过这条道路的花费为 w
样例输入
3 1 1
0 1 1
0 2 1
3 1 3
0 1 1
1 2 1
样例输出
3
4
首先每条树边只会经过1或2次,这个似乎十分显然,证明可以用欧拉回路的判定来证。
而传送相当于是添加了一些能且仅能经过一次的边,那么每条边走过的奇偶性与该字数内新加顶点奇偶性相同,因为没有传送因为要来回必然是偶数,两个顶点都在子树内的传送对这条边没有意义,有一次传送就会是奇数。
所以(fa[x],x)这条边会经过2-(x子树内新加点个数)%2次。
那么我们只要记f[a][b]为a子树内有b个新加顶点的代价,直接树上背包转移即可。
最后显然用f[1][2*x]+x*c更新答案即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 typedef long long lol; 8 struct Node 9 { 10 int next,to; 11 lol dis; 12 }edge[4001]; 13 int num,head[2001],n,k; 14 lol c,f[2001][4001],tmp[4001],size[2001],ans; 15 void add(int u,int v,lol dis) 16 { 17 num++; 18 edge[num].next=head[u]; 19 head[u]=num; 20 edge[num].to=v; 21 edge[num].dis=dis; 22 } 23 void dfs(int x,int pa) 24 {int i,j,l; 25 size[x]=0;f[x][0]=0; 26 for (i=head[x];i;i=edge[i].next) 27 { 28 int v=edge[i].to; 29 if (v==pa) continue; 30 dfs(v,x); 31 for (j=0;j<=size[x]+size[v]+1;j++) 32 tmp[j]=2e14; 33 for (j=0;j<=size[x];j++) 34 { 35 for (l=0;l<=size[v];l++) 36 { 37 tmp[j+l]=min(tmp[j+l],f[x][j]+f[v][l]+edge[i].dis*(2-(l&1))); 38 } 39 } 40 size[x]+=size[v]; 41 for (j=0;j<=size[x];j++) 42 f[x][j]=tmp[j]; 43 } 44 size[x]++; 45 f[x][size[x]]=2e9; 46 for (i=size[x];i>=1;i--) 47 f[x][i]=min(f[x][i],f[x][i-1]); 48 } 49 int main() 50 {int u,v,i; 51 lol w; 52 while (cin>>n>>k>>c) 53 {num=0; 54 memset(head,0,sizeof(head)); 55 memset(f,127/2,sizeof(f)); 56 for (i=1;i<=n-1;i++) 57 { 58 scanf("%d%d%lld",&u,&v,&w); 59 u++;v++; 60 add(u,v,w);add(v,u,w); 61 } 62 dfs(1,0); 63 ans=2e14; 64 for (i=0;i<=k;i++) 65 ans=min(ans,f[1][2*i]+i*c); 66 cout<<ans<<endl; 67 } 68 }