HDU 4313 Matrix
水题:在一个双连通的树上有一些点很有破坏性,我们要把这些带破环性的点隔开,就是破坏一些边使这些点之间不连通,破坏一条边需要一点时间,问最少需要多少时间(同一时间只能破坏一个地方,且忽略位置转移的时间);
首先我们应注意到这是一棵树!俩个点之间的通路是位唯一的:
解法:我们从大边开始枚举如果这条边 的左边和右边 都“连接”一个破坏点(俩个点的通路上除了这个边都被枚举过了);那么这个边在这个链上是最小的! 对于只有俩个破坏点的无疑是最优解;
但当多了一些点呢??对于这个链没有交集的无疑不用考虑,对于有交集的另一个链选取的这个边要也在另一条链上才会有影响!而如果在另一条链上无疑也破坏了另一个关系,(虽然这个边在另一条链上不一定是最小的,但这个边对于原来的点对是必须的也是最小的,而且破坏了另一个,所以是最优的,一箭多雕)。
具体实现:
这个题的做法无疑缩点大法:并查集!对吧?
#include <cstring> #include <algorithm> #include <cstdlib> #include <cstdio> #include <cmath> #include <iostream> typedef long long LL; using namespace std; const int INF=0x7fffffff; struct info { int x,y,dis; bool operator < (const info & rht )const { return dis>rht.dis; } }ko[100005]; bool flag[100005]; int fa[100005]; int n,k; void inint() { memset(flag,false,sizeof(flag)); for(int i=0;i<n;i++) fa[i]=i; } int Find(int x) { return x==fa[x]? x: fa[x]=Find(fa[x]); } void Union(int x,int y) { fa[x]=y; } int main() { int t,tmp; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&k); inint(); for(int i=0;i<n-1;i++) scanf("%d%d%d",&ko[i].x,&ko[i].y,&ko[i].dis); sort(ko,ko+n-1); for(int i=0;i<k;i++) { scanf("%d",&tmp); flag[tmp]=true; } LL ans=0; for(int i=0;i<n-1;i++) { int fa=Find(ko[i].x); int fb=Find(ko[i].y); if(flag[fa]&&flag[fb]) ans+=ko[i].dis; else if(flag[fa]) Union(fb,fa); else if(flag[fb]) Union(fa,fb); else Union(fa,fb); } cout<<ans<<endl; } }