题意:给定一棵带边权的树,求从任意一点出发,访问不同的n-k个点,然后回到起点的最小边权和。
这题在PKUSC中考到了,当时大概有一半的人A了,我当时感觉毫无思路...回家后又冷静的想了想,然后就搞出来了,感觉思路还是很妙的吧..
首先我们考虑如果要访问所有的点,那答案显然是边权和的2倍,那么现在只要访问n-k个点,那么我们就容易想到找一个边权和最小的,包含n-k个点的连通块,那么答案就是这个连通块边权和的2倍。那么我们怎么找呢,我最初想到的是按照kruscal的算法,按边权从小到大加边,用并查集维护一下块的大小,当超过n-k后再删除一些点,后来感觉这样搞的正确性无法保证,因为删除的时候是有限制的,只能删除度为1的点,否则连通性就会被破坏,这样就不一定保证最优了。
那么既然我们考虑加边不好搞,而且刚刚已经发现了删边的条件,那么我们就可以考虑从这棵树中删去一些边,使其成为包含n-k个点的连通块,我们每次选取一个度为1的,连的边权最大的点删掉,然后更新一下其他点的度,重复k次,这样就得到了那个连通块,答案即为边权和乘2
upd: 发现自己做法是错的,这数据也太水了..这样做的问题是如果有一个边权特别大,但它的度不是一,那么我们如果能删掉这条边就删掉这条边,但上述贪心不一定会这么做...正确的做法应该是树形DP,dp[i][j]表示以i为根的子树删了j个点的最小权值和,然后转移一波就可以了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 using namespace std; 6 #define maxn 20005 7 typedef long long LL; 8 int pre[maxn],last[maxn],other[maxn],len[maxn]; 9 int n,k,T,l,du[maxn]; 10 LL ans; 11 bool flag[maxn]; 12 struct Vergil 13 { 14 int x,key; 15 bool operator < (const Vergil &a) const 16 { 17 return key<a.key; 18 } 19 }; 20 priority_queue<Vergil> Q; 21 22 inline int read(void) 23 { 24 int x=0; 25 char ch=getchar(); 26 while (ch>'9'||ch<'0') ch=getchar(); 27 while (ch>='0'&&ch<='9') 28 { 29 x=x*10+ch-'0'; 30 ch=getchar(); 31 } 32 return x; 33 } 34 35 void connect(int x,int y,int z) 36 { 37 l++; 38 pre[l]=last[x]; 39 last[x]=l; 40 other[l]=y; 41 len[l]=z; 42 } 43 44 int main() 45 { 46 T=read(); 47 while (T--) 48 { 49 n=read();k=read(); 50 memset(flag,0,sizeof flag); 51 memset(last,0,sizeof last); 52 memset(du,0,sizeof du); 53 ans=l=0; 54 for (int i=1;i<n;i++) 55 { 56 int x=read(),y=read(),z=read(); 57 x++;y++; 58 ans+=z; 59 connect(x,y,z); 60 connect(y,x,z); 61 du[x]++;du[y]++; 62 } 63 while (!Q.empty()) Q.pop(); 64 for (int i=1;i<=n;i++) 65 if (du[i]==1) 66 for (int p=last[i];p;p=pre[p]) 67 Q.push((Vergil){i,len[p]}); 68 while (k--) 69 { 70 Vergil tmp=Q.top();Q.pop(); 71 ans-=tmp.key; 72 int u=tmp.x;flag[u]=1; 73 for (int p=last[u];p;p=pre[p]) 74 { 75 int v=other[p]; 76 if (flag[v]) continue; 77 du[v]--; 78 if (du[v]==1) 79 for (int P=last[v];P;P=pre[P]) 80 if (!flag[other[P]]) Q.push((Vergil){v,len[P]}); 81 } 82 } 83 printf("%lld\n",ans*2); 84 } 85 return 0; 86 }