小h的树

作为绿色的使者,小h有棵nn个节点的带权树。

找出kk个点A1,A2,,AkA1,A2,⋯,Ak。

使得k1i=1dis(Ai,Ai+1)∑i=1k−1dis(Ai,Ai+1)最小。

其中dis(i,j)dis(i,j)表示点ii到点jj的树上最短路。

输入格式

第一行两个正整数n,kn,k,表示树的顶点数和需要选出的点个数。

接下来n1n−1行每行33个非负整数x,y,zx,y,z,表示从存在一条从xx到yy权值为zz的边。

1kn1≤k≤n。

1x,yn1≤x,y≤n。

1z1051≤z≤105。

输出格式

一行一个整数,表示最小的距离和。

样例一

input
5 4
1 2 1
2 3 3
2 4 2
4 5 5

output

 

7

 

数据范围

对于10%10%的数据,  n10  n≤10。

对于另外10%10%的数据, k=n k=n。

对于另外20%20%的数据, n50 n≤50。

对于另外20%20%的数据, n200 n≤200。

对于100%100%的数据,  n3000  n≤3000。

时间限制:1s1s

空间限制:256MB

思路:由于当n=k的时候,答案就是边长的两倍减去直径。

由于n最多只有3000,所以我们做一个树形dp。

dp[i][j][0]表示以i为根的子树中选择了j个点作为联通块,最小的总边长的两倍

dp[i][j][1]表示以i为根的子树中选择了j个点作为联通块,当直径是i到下面的某个点时候的答案

dp[i][j][2]表示以i为根的子树中选择了j个点作为联通块,此时的答案。

时间复杂度:O(N^2),感觉这个时间复杂度不是很对啊!!!orz

 1 #include<bits/stdc++.h>
 2 
 3 using namespace std; 
 4 
 5 #define F(i,a,b) for(int i=a;i<=b;i++) 
 6 #define D(i,a,b) for(int i=a;i>=b;i--) 
 7 #define ms(i,a)  memset(a,i,sizeof(a)) 
 8 
 9 template <class T> void read(T &x){
10     x=0; int w=0; char c=getchar(); 
11     while (c<'0' || c>'9' )  w+=c=='-',c=getchar(); 
12     while (c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); 
13     if(w) x=-x;   
14 }
15 
16 template <class T> void chkmin(T &x,T y) {x=min(x,y);}
17 template <class T> void chkmax(T &x,T y) {x=max(x,y);}
18 
19 template <class T >void write( T x){
20     if(x<0) x=-x,putchar('-'); 
21     if(x>9) write(x/10); 
22     putchar(x% 10+48); 
23 }
24 
25 template <class T >void writeln(T x){
26     write(x); 
27     putchar('\n');  
28 }
29 int const maxn=3003; 
30 int const inf=1e9;  
31 struct Edge{
32     int to,nt,w;  
33 }E[maxn<<1]; 
34 
35 int n,k,sz[maxn],dp[maxn][maxn][3],H[maxn],cnt; 
36 
37 void add(int a,int b,int c){
38     E[cnt]=(Edge){b,H[a],c}; H[a]=cnt++; 
39 }
40 
41 void merge(int x,int y,int len){
42     F(i,sz[x]+1,sz[x]+sz[y]) dp[x][i][0]=dp[x][i][1]=dp[x][i][2]=inf; 
43     D(i,sz[x],1) F(j,1,sz[y]){
44         chkmin(dp[x][i+j][0],dp[x][i][0]+dp[y][j][0]+len*2); 
45         chkmin(dp[x][i+j][1],dp[x][i][1]+dp[y][j][0]+len*2); 
46         chkmin(dp[x][i+j][1],dp[x][i][0]+dp[y][j][1]+len); 
47         chkmin(dp[x][i+j][2],dp[x][i][2]+dp[y][j][0]+len*2); 
48         chkmin(dp[x][i+j][2],dp[x][i][1]+dp[y][j][1]+len); 
49         chkmin(dp[x][i+j][2],dp[x][i][0]+dp[y][j][2]+len*2);  
50     }
51     sz[x]+=sz[y];  
52 }
53 void dfs(int x,int fa){
54     sz[x]=1; 
55     for(int i=H[x];i!=-1;i=E[i].nt){
56         int v=E[i].to; 
57         if(v==fa) continue; 
58         dfs(v,x); 
59         merge(x,v,E[i].w); 
60     }
61 }
62 int main(){
63     read(n); 
64     read(k); 
65     ms(-1,H); 
66     F(i,1,n-1){
67         int x,y,z; 
68         read(x);read(y); read(z); 
69         add(x,y,z); 
70         add(y,x,z); 
71     }
72     dfs(1,1); 
73     int ans=inf; 
74     F(i,1,n) if(sz[i]>=k) chkmin(ans,dp[i][k][2]); 
75     writeln(ans); 
76     return 0; 
77 }

 

转载于:https://www.cnblogs.com/ZJXXCN/p/9767515.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值