posts - 5,  comments - 5,  trackbacks - 0
     把自己原来做过的几道感觉不错的图论题贴过来。
     
[无向图点双][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==0break;
 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 阅读(271) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2011年4月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿(1)

随笔分类

随笔档案

队员

最新评论

  • 1. re: 杭州G题的代码
  • @此最相思
    271763295,最近事情有点多回复晚了不好意思
  • --fatboy_cw
  • 2. re: 杭州G题的代码
  • 您有QQ么 在线请教一下 您的代码我好几个没看懂...
  • --此最相思
  • 3. re: 杭州G题的代码
  • @OpenWings
    这题是不是求经过几个连通分量?
  • --此最相思
  • 4. re: 杭州G题的代码
  • @此最相思
    对无向图收缩点双连通分量以后,把每个分量连接到对应割点上,对于询问用tarjan处理lca(rmq貌似还得加个虚根),然后用距离除2即可。
  • --OpenWings
  • 5. re: 杭州G题的代码
  • 缩点以后怎么处理 能说的详细些么? 希望能举个具体例子说说 谢谢
  • --此最相思

阅读排行榜

评论排行榜