把自己原来做过的几道感觉不错的图论题贴过来。
[无向图点双][POJ2942]Knights of the Round Table
题目大意:有N个骑士,给出有两两之间有仇恨的关系,要求安排一种环形座次使得总人数为奇数而且其实之间不会发生冲突。
题解:首先根据给出的关系可以确定两两之间没有仇恨的关系,然后根据该关系构建图。容易知道,最后安排的人必定在同一个点双连通分量当中(否则不会形成环)。然后要在每个点双当中寻找一个奇圈。直接给出两个霸气的定理:1.如果一个点双连通分量中存在奇圈,那么整个点双连通分量的所有点必定也在一个奇圈中。
2.一个点双连通分量如果不是二分图则一定包含奇圈
1#include <iostream>
2#include <cstdio>
3#include <algorithm>
4#include <vector>
5#include <stack>
6using namespace std;
7int N,M;
8bool discnt[1005][1005],Stay[1005];
9vector<int> adj[1005];
10int Color,Time,dfn[1005],low[1005],color[1005],bw[1005],Ans;
11stack< pair<int,int> > Stack;
12
13bool isDiv(int u,int _bw){
14 bw[u]=_bw;
15 int i,node;
16 for(i=0;i<adj[u].size();i++){
17 node=adj[u][i];
18 if(color[node]==Color){
19 if(bw[node]==-1){
20 if(!isDiv(node,!_bw)) return false;
21 }
22 else if(bw[node]==_bw) return false;
23 }
24 }
25 return true;
26}
27
28void dfs(int u,int fa){
29 int i,j,node;
30 dfn[u]=low[u]=++Time;
31 for(i=0;i<adj[u].size();i++){
32 node=adj[u][i];
33 if(!dfn[node]){
34 Stack.push(make_pair(u,node));
35 dfs(node,u);
36 low[u]=min(low[u],low[node]);
37 if(low[node]>=dfn[u]){
38 ++Color;
39 pair<int,int> edge_1=make_pair(node,u);
40 pair<int,int> edge_2=make_pair(u,node);
41 while(Stack.top()!=edge_1&&Stack.top()!=edge_2&&!Stack.empty()){
42 color[Stack.top().first]=color[Stack.top().second]=Color;
43 Stack.pop();
44 }
45 color[Stack.top().first]=color[Stack.top().second]=Color;
46 Stack.pop();
47 memset(bw,-1,sizeof(bw));
48 if(!isDiv(u,1)){
49 for(j=1;j<=N;j++){
50 if(color[j]==Color){
51 Stay[j]=true;
52 }
53 }
54 }
55 }
56 }
57 else if(node!=fa){
58
59 low[u]=min(low[u],dfn[node]);
60 }
61 }
62}
63
64
65int main(){
66
67 while(scanf("%d%d",&N,&M)!=EOF){
68 if(N+M==0) break;
69 int i,j,a,b;
70 for(i=1;i<=N;i++) adj[i].clear();
71 memset(discnt,false,sizeof(discnt));
72 for(i=1;i<=M;i++){
73 scanf("%d%d",&a,&b);
74 discnt[a][b]=discnt[b][a]=true;
75 }
76 for(i=1;i<=N;i++){
77 for(j=1;j<=N;j++){
78 if(i==j) continue;
79 if(!discnt[i][j]){
80 adj[i].push_back(j);
81 }
82 }
83 }
84 memset(dfn,0,sizeof(dfn));
85 memset(low,0,sizeof(low));
86
87 memset(Stay,0,sizeof(Stay));
88 memset(color,0,sizeof(color));
89 Stack=stack< pair<int,int> >();
90 Color=0;
91 for(i=1;i<=N;i++){
92 if(!dfn[i]){
93 Time=0;
94 dfs(i,0);
95 }
96 }
97 Ans=N;
98 for(i=1;i<=N;i++) if(Stay[i]) Ans--;
99 printf("%d\n",Ans);
100 }
101 return 0;
102}
校赛时的I题,Special Fish
困惑的地方:比赛时候我们想到KM,然后想起一句话,KM只能做完备匹配,于是没有写。后来发现大家都是直接一遍KM就过了,回来我写成费用流发现不行。答案不应该是最后的cost而应该是 Max(cost),然后被tclsm大牛质疑了,于是有如下讨论研究:
关键点:这个图不是完备匹配。
1)为什么KM可以直接做就AC了?KM做出来不是完备匹配么?NO!KM做出来还是完备匹配,不过对于非完备的用边权为0的边代替了。那么去掉这些边权为0的边可以么?答案是不可以,见下文
2)用费用流做:
对于每个点i,我拆成了i和i'然后用了两种方式建图
1.s-> all i cost =0 cap=1
all i'->t cost =0 cap=1
所有i可以攻击j的 i->j' cost=-(vali^valj) cap=1
做费用流,wa掉,部分比答案小
2.s-> all i cost =0 cap=1
all i'->t cost =0 cap=1
所有i可以攻击j的 i->j' cost=vali^valj cap=1
所有i不可以攻击j的 i->j' cost=0 cap=1
做费用流 AC
为什么会出现这种问题?由于我们做的是最小费用最大流,所以前提条件是流量最大,之后费用最小。于是这个题的关键点终于出来了--最小费用的情况下是不是保证最大流?直观的看貌似是的,因为如果最小费用的情况不是最大流的话,那么由于费用都是负数,可以再找一条增广路来减小费用。直接看这个好像没有什么问题,而且之前和zz讨论他也以这个为论点。但是在说这句话的时候忘记了一个很重要的问题--后向边!!!从后向边走费用一定是负数么?No!所以这么下来未必会减少费用而最后cost也未必是答案。
改进方法:1.使用模型2是的原图可以达到最大流 2.取Max (cost)
感谢tclsm大牛的指点,感谢好友zz的讨论~
POJ 3177 边双连通分量
题目大意:给一个无向图,求最少添加多少条边可以使得任意两个顶点间至少有两条不相交的路径。
题解:如果存在两个顶点,他们之间只有一条路径或者所有路径相交,那么必定有桥。所以对于原图收缩边双连通分量,然后形成一棵树,答案就是树中(叶子节点个数+1)/2 (正确性:不会严格证明,画个图叶子之间两两连一下感觉没问题...)
1#include <iostream>
2#include <vector>
3using namespace std;
4int t,f,r,dfn[5001],low[5001],bic[5001],color,indo[5001],ans;
5bool brige[5001][5001];
6vector<int> adj[5001];
7vector< pair<int,int> > Brige;
8
9void dfs(int u,int v){
10 dfn[u]=low[u]=++t;
11 int node;
12 for(int i=0;i<adj[u].size();i++){
13 node=adj[u][i];
14 if(!dfn[node]){
15 dfs(node,u);
16 low[u]=min(low[u],low[node]);
17 if(low[node]>dfn[u]){
18 brige[node][u]=brige[u][node]=true;
19 Brige.push_back(make_pair(u,node));
20 }
21 }
22 else if(node!=v){
23 low[u]=min(low[u],dfn[node]);
24 }
25 }
26}
27
28void floodfill(int u){
29 bic[u]=color;
30 int node;
31 for(int i=0;i<adj[u].size();i++){
32 node=adj[u][i];
33 if(!bic[node]&&!brige[u][node]){
34 floodfill(node);
35 }
36 }
37}
38
39
40int main(){
41 scanf("%d%d",&f,&r);
42 int i,j,a,b;
43 for(i=1;i<=r;i++){
44 scanf("%d%d",&a,&b);
45 adj[a].push_back(b);
46 adj[b].push_back(a);
47 }
48 dfs(1,0);
49 for(i=1;i<=f;i++){
50 if(!bic[i]){
51 ++color;
52 floodfill(i);
53 }
54 }
55 for(i=0;i<Brige.size();i++){
56 indo[bic[Brige[i].first]]++;
57 indo[bic[Brige[i].second]]++;
58 }
59 for(i=1;i<=color;i++){
60 if(indo[i]==1) ans++;
61 }
62 printf("%d\n",(ans+1)/2);
63 return 0;
64}
posted on 2010-08-01 19:55
OpenWings 阅读(298)
评论(0) 编辑 收藏 引用