图论进阶训练

残暴的逊哥又来血腥镇压我们了,然后给我们布置了很多图论题

有不少也是我没做过的,而且还学了不少新东西,撸完要好好记录一发

uva 11324

题意:这题貌似是给你一个有向图,然后定义一个东西叫clique,就是里面任意两点之间都有一条路,从u->v或者是v->u。问你最大的clique里有多少个点

题解:这题就是弱连通啊,先用tarjan缩点,然后从入读0的SCC开始dfs一条路径下去,看clique里最多有多少个点


#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX         1005
#define   MAXN        50005
#define   maxnode     1005
#define   sigma_size  4
#define   lson        l,m,rt<<1
#define   rson        m+1,r,rt<<1|1
#define   lrt         rt<<1
#define   rrt         rt<<1|1
#define   mid         int m=(r+l)>>1
#define   LL          long long
#define   ull         unsigned long long
#define   mem(x,v)    memset(x,v,sizeof(x))
#define   lowbit(x)   (x&-x)


const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;
const LL     mod   = (1<<64);

/**************¶ÁÈëÍâ¹Ò*********************/
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }
    while(isdigit(tmp=getchar()));
    return ret;
}
/*******************************************/

struct Edge{
    int v,next;
}edge[MAXN];
int head[MAX];
int dfn[MAX];
int low[MAX];
int instack[MAX];
int sstack[MAX];
int belong[MAX];
int in[MAX];
int vis[MAX];
int num[MAX];
int tot,Index,top,cnt,maxn;
vector<int> v[MAX];

void init(){
    mem(head,-1);
    mem(dfn,0);
    mem(low,0);
    mem(instack,0);
    mem(belong,0);
    mem(num,0);
    mem(in,0);
    tot=0;
    Index=0;
    top=0;
    cnt=0;
    maxn=0;
}

void add_edge(int a,int b){
    edge[tot]=(Edge){b,head[a]};
    head[a]=tot++;
}

void tarjan(int u){
    dfn[u]=low[u]=++Index;
    instack[u]=1;
    sstack[++top]=u;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v]) low[u]=min(dfn[v],low[u]);
    }
    if(low[u]==dfn[u]){
        cnt++;
        while(1){
            int k=sstack[top--];
            belong[k]=cnt;
            instack[k]=0;
            num[cnt]++;
            if(k==u) break;
        }
    }
}

void dfs(int u,int tmp){
    if(maxn<tmp) maxn=tmp;
    int n=v[u].size();
    if(n==0) return ;
    for(int i=0;i<n;i++){
        int k=v[u][i];
        if(!vis[k]){
            vis[k]=1;
            dfs(k,tmp+num[k]);
            vis[k]=0;
        }
    }
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        init();
        for(int i=0;i<m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add_edge(a,b);
        }
        for(int i=1;i<=n;i++){
            if(!dfn[i]) tarjan(i);
        }
        for(int i=1;i<=cnt;i++) v[i].clear();
        for(int i=1;i<=n;i++){
            for(int j=head[i];j!=-1;j=edge[j].next){
                int vv=edge[j].v;
                if(belong[i]!=belong[vv]){
                    in[belong[vv]]++;
                    v[belong[i]].push_back(belong[vv]);
                }
            }
        }
        for(int i=1;i<=cnt;i++){
            if(!in[i]){
                mem(vis,0);
                vis[i]=1;
                dfs(i,num[i]);
            }
        }
        printf("%d\n",maxn);
    }
    return 0;
}


uva 11396

题意:这题貌似是给你一个图,每个点有3个度,然后问你能不能把这张图看成若干个爪子形状

题解:这题我一直没怎么读明白,看了题解很多题解也都没说清楚,就说个二分图

首先这题,可以把爪子中心的点看成一种关键点,爪子边上的点看成非关键点,然后每个爪子都有三个度,所以就是关键点绝对连着三个非关键点,非关键点必须连着三个关键点,不然没法把这图分成若干个爪子。(其实我还是不太明白题目到底说的怎么分割)

按照这个意思,就是判定二分图啊,就是染色问题

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX         1005
#define   MAXN        50005
#define   maxnode     1005
#define   sigma_size  4
#define   lson        l,m,rt<<1
#define   rson        m+1,r,rt<<1|1
#define   lrt         rt<<1
#define   rrt         rt<<1|1
#define   mid         int m=(r+l)>>1
#define   LL          long long
#define   ull         unsigned long long
#define   mem(x,v)    memset(x,v,sizeof(x))
#define   lowbit(x)   (x&-x)


const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;
const LL     mod   = (1<<64);

/**************¶ÁÈëÍâ¹Ò*********************/
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }
    while(isdigit(tmp=getchar()));
    return ret;
}
/*******************************************/

struct Edge{
    int v,next;
}edge[MAXN];
int head[MAX];
int col[MAX];
int tot;

void init(){
    mem(head,-1);
    mem(col,0);
    tot=0;
}

void add_edge(int a,int b){
    edge[tot]=(Edge){b,head[a]};
    head[a]=tot++;
}

bool dfs(int u){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!col[v]){
            col[v]=-col[u];
            if(!dfs(v)) return false;
        }
        else if(col[v]==col[u]) return false;
    }
    return true;
}

int main(){
    int n;
    while(scanf("%d",&n)&&n){
        int a,b;
        init();
        while(scanf("%d%d",&a,&b)&&a){
            add_edge(a,b);
            add_edge(b,a);
        }
        int flag=0;
        for(int i=1;i<=n;i++){
            if(!col[i]){
                col[i]=1;
                if(!dfs(i)){
                    flag=1;
                    break;
                }
            }
        }
        if(flag) printf("NO\n");
        else printf("YES\n");
    }
    return 0;
}

uva 10765

题意:又是一个看不懂题目意思的题,这个题就是给你一个无向连通图,然后问你去掉一个点,图变成多少个连通分量,问你前m中去点方法

题解:建图,tarjan求割点,求每个点下面连着几个儿子。也是比较明显的

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX         10005
#define   MAXN        1000005
#define   maxnode     1005
#define   sigma_size  4
#define   lson        l,m,rt<<1
#define   rson        m+1,r,rt<<1|1
#define   lrt         rt<<1
#define   rrt         rt<<1|1
#define   mid         int m=(r+l)>>1
#define   LL          long long
#define   ull         unsigned long long
#define   mem(x,v)    memset(x,v,sizeof(x))
#define   lowbit(x)   (x&-x)


const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;
const LL     mod   = (1<<64);

/**************¶ÁÈëÍâ¹Ò*********************/
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }
    while(isdigit(tmp=getchar()));
    return ret;
}
/*******************************************/

struct Edge{
    int v,next;
}edge[MAXN];
struct Node{
    int id,num;
    bool operator < (const Node &a)const{
        if(num==a.num) return id<a.id;
        return num>a.num;
    }
}node[MAX];
int head[MAX];
int dfn[MAX];
int low[MAX];
int vis[MAX];
int tot,Index,cnt;


void init(){
    mem(head,-1);
    mem(dfn,0);
    mem(low,0);
    mem(vis,0);
    tot=0;
    Index=0;
    cnt=0;
}

void add_edge(int a,int b){
    edge[tot]=(Edge){b,head[a]};
    head[a]=tot++;
}

void tarjan(int u,int fa){
    dfn[u]=low[u]=++Index;
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!vis[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v]) vis[u]++;
        }
        else low[u]=min(dfn[v],low[u]);
    }
    if(fa==-1) node[cnt++]=(Node){u,vis[u]-1};
    if(fa!=-1) node[cnt++]=(Node){u,vis[u]};
}

int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)&&n){
        int a,b;
        init();
        while(scanf("%d%d",&a,&b)){
            if(a==-1&&b==-1) break;
            add_edge(a,b);
            add_edge(b,a);
        }
        for(int i=0;i<n;i++){
            if(!dfn[i]) tarjan(i,-1);
        }
        sort(node,node+cnt);
        for(int i=0;i<m;i++){
            printf("%d %d\n",node[i].id,node[i].num);
        }
        printf("\n");
    }
    return 0;
}


uva 11294;

题意:新郎新娘是0h 0w,然后给你n-1对cp,还有其中m对有奸情(基情),然后新娘不能看见坐在她对面一排的人有奸情,输出新娘这一排的人

题解:百度了才知道是2-SAT,学了一下午,自己写tarjan的拓扑排序写不像,然后就用了大白的上面的模版(lrj大神就是厉害)

首先是建图的问题,2-SAT的具体思想就看博客或者大白好叻

一对cp为i,拆成两个点2*i和2*i+1,女的坐在新娘这一排的为真,记作2*i,男的坐在新娘这一排为假,记作2*i+1

如果输入的是xw yw,就是两个女的有奸情,她们之间必须至少有一个在新娘这排 就是 x V y,(A是且)等价于 ~x=>y A ~y=>x

就在2*x+1 ->2*y和2*y+1->2*x之间建有向边

搜的时候先搜2*i,假设i是真,如果搜到某一对节点都在其中,有一个环,那么就是不可能的,返回false,说明i为真是错的,就搜2*i+1,如果还是错的,那么就是不存在这种分配的方法

wa了次,因为没考虑到有可能有人和新郎新娘有奸情(这尼玛太扯淡)

坐在新娘这排肯定是真,那么新娘0肯定是真,新郎1为假,如果dfs(0)返回false,说明新娘不为真,这可以直接说明不存在正确的分法

这题就当熟悉2-SAT模版

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX         2005
#define   MAXN        40005
#define   maxnode     1005
#define   sigma_size  4
#define   lson        l,m,rt<<1
#define   rson        m+1,r,rt<<1|1
#define   lrt         rt<<1
#define   rrt         rt<<1|1
#define   mid         int m=(r+l)>>1
#define   LL          long long
#define   ull         unsigned long long
#define   mem(x,v)    memset(x,v,sizeof(x))
#define   lowbit(x)   (x&-x)


const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;
const LL     mod   = 9901;

/**************¶ÁÈëÍâ¹Ò*********************/
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }
    while(isdigit(tmp=getchar()));
    return ret;
}
/*******************************************/

struct TwoSAT{
    int n;
    struct Edge{
        int v,next;
    }edge[MAXN];
    int head[MAX];
    int tot;
    bool mark[MAX];
    int S[MAX];
    int c;

    bool dfs(int x){
        if(mark[x^1]) return false;
        if(mark[x]) return true;
        mark[x]=true;
        S[c++]=x;
        for(int i=head[x];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(!dfs(v)) return false;
        }
        return true;
    }

    void init(int n){
        this->n=n;
        mem(head,-1);
        mem(mark,0);
        tot=0;
    }

    void add_edge(int a,int b){
        edge[tot]=(Edge){b,head[a]};
        head[a]=tot++;
    }

    bool solve(){
        for(int i=0;i<n*2;i+=2){
            if(!mark[i]&&!mark[i+1]){
                c=0;
                if(!dfs(i)){
                    if(i==0) return false;
                    while(c>0) mark[S[--c]]=false;
                    if(!dfs(i+1)) return false;
                }
            }
        }
        return true;
    }
}sat;

char get[30];

int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)){
        if(n==0&&m==0) break;
        sat.init(n);
        for(int i=0;i<m;i++){
            int a,b;
            char c,d;
            scanf("%d%c%d%c",&a,&c,&b,&d);
            if(c=='h'&&d=='h'){
                sat.add_edge(2*a,2*b+1);
                sat.add_edge(2*b,2*a+1);
            }
            if(c=='w'&&d=='h'){
                sat.add_edge(2*a+1,2*b+1);
                sat.add_edge(2*b,2*a);
            }
            if(c=='h'&&d=='w'){
                sat.add_edge(2*a,2*b);
                sat.add_edge(2*b+1,2*a+1);
            }
            if(c=='w'&&d=='w'){
                sat.add_edge(2*a+1,2*b);
                sat.add_edge(2*b+1,2*a);
            }
        }
        if(sat.solve()){
            for(int i=1;i<n;i++){
                if(sat.mark[2*i]) printf("%dw",i);
                else printf("%dh",i);
                if(i==n-1) printf("\n");
                else printf(" ");
            }
        }
        else printf("bad luck\n");
    }
    return 0;
}

uva 11972

题意:给你一个无向图,然后问你要添加多少条边才能让这个图变成双联通

题解:这题和以前做过的不一样的地方在于:原本给的图不一定是连通的,有可能缩点之后会有孤立点,其实也就最后的公式和以前的不一样

缩点之后,度为1的点为叶子节点有A个,度为0的节点有B个,公式就是(A+1)/2+B,如果图原本就是双连通的,那么答案就是0(因为这个wa了好几次,因为如果原本就是双连通的,B=1,那么公式答案就是1,就跪了)

公式的话自己画图看看就行了(A+1)/2是原本如果只有叶子节点的公式,如果存在一些孤立点,只要把孤立点,塞进你两个叶子节点连接的边之间(就等于多加一条边)

果然一边打游戏一边写代码正确率很低,而且窝在宿舍这种小地方写代码正确率确实不行,还是喜欢宽敞的组里啊

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           1005
#define   MAXN          200005
#define   maxnode       1005
#define   sigma_size    4
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   mid           int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>


const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;
const LL     mod   = (1<<64);

/**************¶áèëía1ò*********************/
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }
    while(isdigit(tmp=getchar()));
    return ret;
}
/*******************************************/

struct Edge{
    int v,next;
}edge[MAX*MAX];
int head[MAX];
int dfn[MAX];
int low[MAX];
int instack[MAX];
int sstack[MAX];
int belong[MAX];
int in[MAX];
int tot,Index,cnt,top;

void add_edge(int a,int b){
    edge[tot]=(Edge){b,head[a]};
    head[a]=tot++;
}
void init(){
    mem(head,-1);
    mem(dfn,0);
    mem(low,0);
    mem(in,0);
    mem(belong,0);
    mem(instack,0);
    top=0;
    cnt=0;
    tot=0;
    Index=0;
}

void tarjan(int u,int fa){
    dfn[u]=low[u]=++Index;
    instack[u]=1;
    sstack[++top]=u;
    int flag=0;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==fa&&!flag){
            flag=1;
            continue;
        }
        if(!dfn[v]){
            tarjan(v,u);
            low[u]=min(low[v],low[u]);
        }
        else if(instack[v]) low[u]=min(dfn[v],low[u]);
    }
    if(low[u]==dfn[u]){
        cnt++;
        while(1){
            int k=sstack[top--];
            instack[k]=0;
            belong[k]=cnt;
            if(k==u) break;
        }
    }
}

int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        init();
        for(int i=0;i<m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add_edge(a,b);
            add_edge(b,a);
        }
        for(int i=1;i<=n;i++){
            if(!dfn[i]) tarjan(i,-1);
        }
        if(cnt==1){
            printf("0\n");
            continue;
        }
        for(int i=1;i<=n;i++){
            for(int j=head[i];j!=-1;j=edge[j].next){
                int v=edge[j].v;
                if(belong[i]!=belong[v]){
                    in[belong[i]]++;
                }
            }
        }
        int ans1=0,ans2=0;
        for(int i=1;i<=cnt;i++){
            if(in[i]==1) ans1++;
            if(in[i]==0) ans2++;
        }
        printf("%d\n",(ans1+1)/2+ans2);
    }
    return 0;
}


你可能感兴趣的:(ACM)