老规矩,先上原题
题目大意就是将一个图的节点拆分成尽可能多的若干块连通分量,并且任意两块分量间要满足以下性质:
D(C1,C2)>H(C1) or D(C1,C2)>H(C2)
其中:D=连接C1,C2的所有边中权值最小的边的权值 H=最小生成树的权值最大边的权值+常数c/块内的点数
这里要注意是大于其中一个任意一个就行了,文中用词是any而不是both
这题乍一看毫无思路,借鉴了http://blog.csdn.net/jtjy568805874/article/details/53435235的逆向思维做法后豁然开朗!
不过这哥们居然证明都略了,我还是费了点脑子的:
一开始每个节点都独立成块。
把所有边按权值从小到大排序(可以看出就是kruskal的变型)。
按权值从小到大依次取出一条边e:
如果边的两端分别属于不同的块C1,C2,则判断如果不连上这条边,C1,C2是否会不满足支持他们相互独立的条件e.weight>=D(C1,C2)>H(C1) or D(C1,C2)>H(C2),用如果不满足就合并这两个块。
所有边取完了,结果也就出来了。
这个算法之所以正确,精妙之处在于由于是从小到大取,当前这条边(e)权值必然比以后所有边都小的,所有用于连接(合并本来的不合并的块)的边的必然恰好构成最小生成树,且任意两块之间最多只有一条边相连,因此只要当前的C1,C2由于这条边e的存在无法满足独立性,假设后续某一时刻t由于某种方式C1,C2两块东西重新分离开了,分离的可能性:
1.新出现块C3,C2与C3合并为C23。C1与C23这两块竟然可以相互独立,须满足e>c/(C(C2)+C(C3))+最大边(C23);
2.新出现块C3,C1与C3合并为C13。C1与C13这两块竟然可以相互独立,须满足e>c/(C(C1)+C(C3))+最大边(C13);
3.新出现块C3,C4,C23与C14相互独立,且C23合并发生在C14之前,且C14连接出现之前C23和C1不能相互独立
4.新出现块C3,C4,C23与C14相互独立,且C14合并发生在C23之前,且C23连接出现之前C14和C2不能相互独立
由于1,2对称我们先证明情况1
设C23发生合并的边为e2,则e2>=C23中所有的边,更大于等于e(因为e2最后加入),则e2>=e>c/(C(C2)+C(C3))+最大边(C23);而此时最大边(C23)==e2,推出了e2>c/(C(C2)+C(C3))+e2,又c为正数,产生矛盾,故情况1,2不成立。
由于3,4对称我们来证明3,设连接C23的边为e2,连接C14的边为e3,则满足e>c/(C(1)+C(4))+最大边(C14),根据题设顺序可得e<=e2<=e3,即e3>c/(C(1)+C(4))+最大边(C14),而此时最大边(C14)==e3,又c为正数,同样产生矛盾,故情况3,4不成立。
综上,只要两块东西由于边e发生相连无法维持其相互独立性,不管是最终两块东西都与其他块发生合并,还是只有其中一块与其他块发生合并,都不会影响e相连导致的这次失去独立性的正确性,
#include "stdafx.h"//提交时去掉此行 #include <stdlib.h> #include <iostream> #include <string> #include <vector> #include <set> #include <algorithm> #include <map> #include <queue> #define rep(i,j,k) for(int i=j;i<=k;i++) #define range(i,j,k) for(int i=j;i<k;i++) #define println3(a,b,c) cout<<a<<" "<<b<<" "<<c<<"\n" using namespace std; const int oo = 999999999; class Edge { public: int v1 = 0, v2 = 0; float weight = 0; void my_read() { cin >> v1 >> v2 >> weight; } void my_write() { println3(v1,v2,weight); } }; class Component { public: void init(int node_index) { bcj_root = node_index; members.resize(1); members[0] = node_index; } vector<int> members;//包含根节点在内 int bcj_root=0;//根节点//排序前自己的下标不等于这个说明自己被并到别人那里了 void write_me() { range(i, 0, members.size()) { if (i==members.size()-1) { cout << members[i]; } else cout << members[i] << " "; } cout << "\n"; } void sort_members() { sort(members.begin(),members.end()); } }; class Graph { public: vector<Edge> edges; vector<int> bcj_root;// vector<Component> components;//连通分量 int bcj_getroot(int i) { if (bcj_root[i]==i) { } else { bcj_root[i] = bcj_getroot(bcj_root[i]); } return bcj_root[i]; } vector<int> mst_edge_weight_max;//下标为连通分量的root的节点序号 void init(int Nv,int Ne) { this->Nv = Nv; this->Ne = Ne; edges.resize(Ne); bcj_root.resize(Nv); components.resize(Nv); mst_edge_weight_max.resize(Nv);//不是根的连通分量可能始终是0 range(i, 0, Nv) { bcj_root[i] = i; components[i].init(i); mst_edge_weight_max[i] = 0; } } int Nv=0; int Ne = 0; void read_edges() { range(i,0,Ne) { edges[i].my_read(); } } void sort_edges() { auto cmp = [](Edge& e1, Edge& e2) { if (e1.weight<e2.weight) { return true; } return false; }; sort(edges.begin(),edges.end(),cmp); } void write_edges() { range(i, 0, Ne) { edges[i].my_write(); } } }; string my_itoa(int x) { char ans_[30]; sprintf(ans_, "%d", x); return string(ans_); } void myoutput(Graph& graph) { vector<Component> components_new; components_new.reserve(graph.Nv); range(i,0,graph.Nv) { if (graph.components[i].bcj_root == i) { graph.components[i].sort_members(); components_new.push_back(graph.components[i]); } } auto cmp = [](Component& c1,Component& c2) {//size至少为1不用担心0没有 if (c1.members[0]<c2.members[0]) { return true; } return false; }; sort(components_new.begin(),components_new.end(),cmp); range(i, 0, components_new.size()) { components_new[i].write_me(); } } void myinput(Graph& graph,float& c) { int Nv = 0, Ne = 0; cin >> Nv >> Ne >> c; graph.init(Nv,Ne); graph.read_edges(); } float func_H(float mew,float c,int C) { return mew + c / ((float)C); } int main() { Graph graph; float c = 0; myinput(graph,c); graph.sort_edges(); //graph.write_edges(); range(i, 0, graph.Ne) { Edge& edge_now = graph.edges[i]; int root1 = graph.bcj_getroot(edge_now.v1); int root2 = graph.bcj_getroot(edge_now.v2); if (root1==root2) { continue;//已经是一伙的了 } float weightnow = edge_now.weight; float func_H_now1 = func_H(graph.mst_edge_weight_max[root1],c,graph.components[root1].members.size()); if (weightnow>func_H_now1) { continue;//说明目前还可以维持分开 } float func_H_now2 = func_H(graph.mst_edge_weight_max[root2], c, graph.components[root2].members.size()); if (weightnow>func_H_now2) { continue; } //说明这条边的加入迫使这两个合并了 graph.bcj_root[root1] = root2; graph.mst_edge_weight_max[root2] = weightnow;//新加的必然最大,其实就是kruskal graph.components[root1].bcj_root = root2; range(j,0, graph.components[root1].members.size()) graph.components[root2].members.push_back(graph.components[root1].members[j]);//nv复杂度?这句话应该不会tle吧 } myoutput(graph); system("pause");//提交时去掉此行 return 0; }
Sample Input 1:
10 21 100
0 1 10
0 3 60
0 4 90
1 2 90
1 3 50
1 4 200
1 5 86
2 4 95
2 5 5
3 4 95
3 6 15
3 7 101
4 5 500
4 6 100
4 7 101
4 8 101
5 7 300
5 8 50
6 7 90
7 8 84
7 9 34
Sample Output 1:
0 1 3 6
2 5 8
4
7 9
Sample Input 2:
7 7 100
0 1 10
1 2 61
2 3 50
3 4 200
4 5 82
5 0 200
3 6 90
Sample Output 2:
0 1
2 3 6
4 5