whu 1471 All Your Bases 暴力删边
题意:
N个顶点(N<=10000),N-1条边,每条边含有一个权值,有T个基地,需要删除K条边,形成K+1个连通块,使每个连通块包含至少一个基地.
求最小花费.
解法:
还是太弱. 看完题没敢想 O(N^2)的算法,最初想建模求最小割,后面又想到点分治,但是O(N^2)的空间复杂度。。。。。
其实题目给了10S, 将所有边从小到大排序后, 尝试删除该边,若此边两端 联通快,都包含基地,则删除. 处理出来K+1个联通块就是答案了.
View Code
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int N = (int)1e4+10; struct Edge{ int a,b,c; void input(){ scanf("%d%d%d",&a,&b,&c); } bool operator <(const Edge tmp) const{ return c < tmp.c; } }edge[N]; int n, t, k, sum; bool used[N], milit[N]; int st[N], r[N]; int st1[N], r1[N]; int find1(int x){ return x == st[x] ? x : (st[x]=find1(st[x])); } int find2(int x){ return x == st1[x] ? x : (st1[x]=find2(st1[x])); } void init(){ for(int i = 1; i <= n; i++){ st1[i] = i, r1[i] = (milit[i] ? 1 : 0); } for(int i = 0; i < n-1; i++){ if( used[i] ){ int x = find2( edge[i].a ), y = find2( edge[i].b ); if( x != y ) st1[x] = y, r1[y] |= r1[x]; } } } int solve(){ for(int i = 1; i <= n; i++) st[i] = i, r[i] = (milit[i] ? 1 : 0); int t_sum = 0, num = 0; for(int i = 0; i < n-1; i++){ int x = find1( edge[i].a ), y = find1( edge[i].b ); st[x] = y; r[y] |= r[x]; t_sum += edge[i].c; used[i] = true; } for(int i = 1; i <= n; i++){ if( i == st[i] ) num++; } if( num < (k+1) ){ for(int i = 0; i < n-1; i++){ int a = edge[i].a, b = edge[i].b; if( num == (k+1) ) break; if( used[i] ){ used[i] = false;//拆边 init(); used[i] = true; //恢复 int x = find2(a), y = find2(b); if( (r1[x] & r1[y]) == 1 ){ //该边被使用,且两个联通块中都包含 基地,所以可以删除,联通块数量+1 t_sum -= edge[i].c; num++; used[i] = false; } } } } return sum-t_sum; } int main(){ int T; scanf("%d", &T); for(int Case = 1; Case <= T; Case++ ){ scanf("%d%d%d",&n,&t,&k); sum = 0; for(int i = 0; i < n-1; i++){ edge[i].input(); sum += edge[i].c; } sort( edge, edge+n-1 ); memset( used, 0, sizeof(used)); memset( milit, 0, sizeof(milit)); for(int i = 0; i < t; i++){ int x; scanf("%d",&x); milit[x] = true; } printf("Case %d: %d\n", Case, solve() ); } return 0; }