图论进阶训练

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

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

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 算法训练篇pdf 是一个提供算法学习资料的电子书。这本书可以帮助算法学习者巩固和提高他们的算法能力。 首先,算法训练篇pdf 的内容丰富多样。它涵盖了各种算法主题,如动态规划、图论、贪心算法等。每个主题都有详细的讲解和示例代码,以帮助读者更好地理解和应用这些算法。 其次,这本书的难度适中。它适合已经具备一定算法基础的学习者一步提高自己的能力。通过学习这本书,读者可以一步提升自己解决复杂算法问题的能力,同时也可以深入了解各种常见的算法思想和技巧。 此外,这本书的编写方式也十分友好。作者在讲解算法原理的同时,还会结合具体的例子和图形行说明,使得读者更容易理解和掌握算法的应用方法。同时,每个章节都有练习题目,可以帮助读者巩固所学的知识并提高实践能力。 总的来说,算法训练篇pdf 是一个很好的算法学习资料。它的内容丰富多样,难度适中,编写方式友好。读者通过学习这本书,可以提高自己的算法能力,掌握各种常见的算法思想和技巧,为解决实际问题提供了有力的工具和思路。 ### 回答2: 《算法训练篇pdf》是一份高级算法训练资料,旨在帮助读者深入学习和理解算法的高级概念和技巧。该资料包含了丰富的算法案例和问题,涵盖了动态规划、图论、字符串处理等多个领域。 这份资料首先从基础的算法知识出发,引导读者逐步。通过详细的讲解和示例代码,读者可以掌握各种高级算法的原理和实现方法。同时,该资料注重实践,提供了大量的练习题目,帮助读者巩固所学知识,并提供了解题思路和解决方法。 《算法训练篇pdf》的特点之一是注重实际应用。它涵盖了大量的真实场景和实际问题,并展示了如何用高级算法解决这些问题。这对于读者来说是具有很大帮助的,因为他们可以更好地理解算法在实际工程中的应用场景和效果。 此外,该资料还提供了丰富的参考文献和扩展阅读材料,帮助读者一步拓宽和深入学习。同时,读者也可以通过该资料获得关于算法训练和职业发展的一些建议和指导。 总之,《算法训练篇pdf》是一份对于想要深入学习高级算法的读者来说非常有价值的资料。它适合那些已经具备了一定算法基础,但希望一步提升能力和应用技巧的人。通过学习这份资料,读者将能够更好地应用高级算法解决实际问题,提高自己的算法能力,并在算法领域取得更高的成就。 ### 回答3: 《算法训练篇》PDF 是一个深入学习算法的教材。该教材通过系统化的讲解和实践编程练习,帮助学习者更加深入地理解和应用算法。 该PDF内容主要涵盖了算法的知识和应用,包括动态规划、贪心算法、图论、高级数据结构、字符串处理等方面的内容。通过这些知识的学习,可以帮助学习者提高算法解决问题的能力,一步提升编程的水平。 在《算法训练篇》PDF中,每个章节都有对应的理论讲解和算法实现的练习题。通过理论的学习,学习者可以掌握算法的核心思想和解题思路。而通过实践编程练习,可以帮助学习者巩固所学知识,提高算法编程的实际应用能力。 《算法训练篇》PDF的特点是内容丰富、逻辑性强、难度适中。无论是想一步提高算法水平的程序员,还是希望在算法竞赛中取得好成绩的学生,都可以通过学习这本教材来达到自己的目标。 总之,《算法训练篇》PDF是一本非常实用和有价值的教材,可以帮助学习者深入学习和应用算法,提高算法解决问题的能力,同时也为其他算法教学提供了参考和指导。推荐有一定算法基础的读者阅读并行实践,相信会有很大的收获。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值