题意:给定一棵带边权的树,求从任意一点出发,访问不同的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 }

 

posted on 2017-05-24 09:50  Vergil_LY  阅读(225)  评论(0编辑  收藏  举报