【省选】二分图

P3386 【模板】二分图匹配

#include<bits/stdc++.h>
#define ll long long
#define ri register int
using namespace std;
ll n,m,e,link[1005][1005],vis[2005],match[2005],ans;
ll inline read(){
    ll f=1,sum=0;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
    return f*sum;
}
bool dfs(ll x){
    for(ri i=n+1;i<=n+m;i++){
        if(link[x][i]&&!vis[i]){
            vis[i]=1;
            if(!match[i]||dfs(match[i])){
                match[i]=x;
                return true;
            }
        }
    }
    return false;
}
int main(){
    n=read();m=read();e=read();
    for(ri i=1;i<=e;i++){
        ll x,y;
        x=read();y=read();
        if(x>n||y>m)continue;
        link[x][n+y]=1;
    }
    for(ri i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        if(dfs(i))ans++;
    }
    printf("%lld",ans);
    return 0;
}
View Code

P1640 [SCOI2010]连续攻击游戏

比较特别的二分图,都能想到把两个属性分为左右端点,但难以解决这道题,于是换一个很妙的思路:把物品的两个属性与编号分别连边,即属性与边为左右端点。

#include<bits/stdc++.h>
#define ri register int
#define ll long long
#define For(i,l,r) for(ri i=l;i<=r;i++)
#define Dfor(i,r,l) for(ri i=r;i>=l;i--)
using namespace std;
const int M=1e6+6;
int n,head[M],cnt,ans,pre[M];
struct node{
    int nxt,to;
}e[M<<1];
bool vis[M];
inline ll read(){
    ll f=1,sum=0;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
    return f*sum;
}
inline void add(int u,int v){
    e[++cnt].nxt=head[u];
    head[u]=cnt;
    e[cnt].to=v;
}
inline bool dfs(int u){
    if(vis[u]) return 0;
    vis[u]=1;
    for(ri i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if((!pre[v])||dfs(pre[v])){
            pre[v]=u;return 1;
        }
    }
    return 0;
}
int main(){
    n=read();
    For(i,1,n){
        int a=read(),b=read();
        add(a,i),add(b,i);
    }
    For(i,1,n){
        memset(vis,0,sizeof(vis));
        if(dfs(i)) ans++;
        else break;
    }
    printf("%d\n",ans);
    return 0;
}
View Code

P1129 [ZJOI2007]矩阵游戏

同样比较妙的一道二分图的题,假如i行j列(即a[i][j])为黑子,那么把j连到i(把i连到j一样),也就是把行列做左右端点,计算出最大匹配,如果==n那么就输出Yes否则No。

*另外就是二分图都差不多,基本只会改两个地方,要么是G[i][j]或e[i].u/v(邻接矩阵/链式前向星),看题目要求要哪个好写;要么就是vis[i]时直接退出还是跳过匹配过程

#include<bits/stdc++.h>
#define For(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
const int N=505;
int n,ans,t,pre[N];
bool G[N][N],vis[N];
inline int read(){
    int f=1,sum=0;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
    return f*sum;
}
inline bool dfs(int x){
    For(i,1,n){
        if(G[x][i]&&!vis[i]){
            vis[i]=1;
            if(!pre[i]||dfs(pre[i])){
                pre[i]=x;return 1;
            }
        }
    }
    return 0;
}
int main(){
    t=read();
    while(t--){
        memset(pre,0,sizeof(pre));
        memset(G,0,sizeof(G));
        ans=0;n=read();
        For(i,1,n){
            For(j,1,n){
                G[i][j]=read();
            }
        }
        For(i,1,n){
            memset(vis,0,sizeof(vis));
            ans+=dfs(i);
        }
        if(ans==n) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
View Code

P1963 [NOI2009]变换序列

如果真的理解了二分图的原理&过程&代码,思路还是很好想的,我之前就是吃了对每个,尤其是基础算法半懂不懂,应用也半熟不会自己完整打完一道题的苦,其实联赛范围内基本也就考这些了,省选不知。

思路就是一个倒着走的二分图,这样能保证字典序最小,我想到了但又同之前一万次一样觉得没有正确性,其实如果没能理解手推一下也能发现它是正确的,前提是推的认真正确。

然后要优先连小点的数字,正确性其实手推一下也能出来。

其实上述写法我都是感性理解觉得行,正确性不会证所以没打,又懒得手推。但事实证明这真的是赌博,还是必须要会自己证,实在实在不行再手推几组数据,考虑情况全面点保证正确

#include<bits/stdc++.h>
#define ri register int
#define ll long long
#define For(i,l,r) for(ri i=l;i<=r;i++)
#define Dfor(i,r,l) for(ri i=r;i>=l;i--)
using namespace std;
const int M=1e4+4;
int n,d[M],pre[M],G[M][3],to[M];
bool vis[M];
inline ll read(){
    ll f=1,sum=0;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
    return f*sum;
}
inline bool dfs(int x){
    For(i,0,1){
        int v=G[x][i];
        if(!vis[v]){
            vis[v]=1;
            if((pre[v]==-1)||dfs(pre[v])){
                pre[v]=x;to[x]=v;return 1;
            }
        }
    }
    return 0;
}
int main(){
    n=read();
    For(i,0,n-1) d[i]=read();
    For(i,0,n-1){
        G[i][0]=(i+d[i]+n)%n,G[i][1]=(i-d[i]+n)%n;
        if(G[i][0]>G[i][1]) swap(G[i][0],G[i][1]);
        pre[i]=-1;
    }
    Dfor(i,n-1,0){
        memset(vis,0,sizeof(vis));
        if(!dfs(i)) {printf("No Answer\n");return 0;}
    }
    For(i,0,n-1) printf("%d ",to[i]);
    return 0;
}
View Code
扫描二维码关注公众号,回复: 7764502 查看本文章

猜你喜欢

转载自www.cnblogs.com/jian-song/p/11804938.html
今日推荐