8/9 基础思维+二分图染色+最大完美匹配KM算法

Posted 钟钟终

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8/9 基础思维+二分图染色+最大完美匹配KM算法相关的知识,希望对你有一定的参考价值。

二分图判定染色法

大佬链接:https://www.bilibili.com/video/BV1sZ4y1i7NZ?spm_id_from=333.999.0.0&vd_source=91973ada1213cf6ba2cbb43a2bebd2e8

定理:二分图不存在奇环,只存在偶数环

#include<bits/stdc++.h>
#define int long long
#define endl '\\n'

using namespace std;
const int N=5e5+5;
const int inf=0x3f3f3f3f;
int n,m,cnt,head[N],color[N];
struct node

    int to,nxt;
e[N];
void add(int from,int to)

    e[++cnt].to=to;
    e[cnt].nxt=head[from];
    head[from]=cnt;

bool dfs(int u,int c) //判断是否是二分图

    color[u]=c;
    for(int i=head[u];i;i=e[i].to)
    
        int v=e[i].to;
        if(!color[v])
            if(dfs(v,3-c)) return 1;
        else if(color[v]==c)
            return 1;
    
    return 0;

signed main()

    cin>>n>>m;
    for(int i=1;i<=m;i++)
    
        int u,v;cin>>u>>v;
        add(u,v),add(v,u);
    
    int flag=1;
    for(int i=1;i<=n;i++)
        if(!color[i])
        
            if(dfs(i,1))
            
                flag=0;
            
        
    if(flag)
        cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;
    return 0;


D. The Number of Imposters

这题属实把我写烦了,代码怎么调都有问题
技巧:
1.主程序放到函数中,会提高编程效率。本题快了将近400ms。
2.N若是e5次方基本,一般定义为700010便可不用管了。
本题思路:
1.若是骗子,说的话一定说明两人是相反阵营。
2.若是正常队员,说的真话,则说明两人一定处于相同阵营。本题学到了一个建立虚点的技巧:采用一个中间点,来进行同类归并

e[u].push_back(g), e[g].push_back(u);
e[g].push_back(v), e[v].push_back(g);

3.要求最大的骗子数,那么对于每一次深搜,两个连通块较大的那个设置为骗子阵营。

#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=700010;
const int inf=0x3f3f3f3f;
int n,m,cnt;
int color[N],mx1,mx2;
vector<int>e[N];
bool dfs(int x, int c)

	color[x] = c;
	if (x <= n)
    
		if (c == 1) mx1++;
		else mx2++;
	
	for (int j : e[x])
	
		if (!color[j])
		
			if (!dfs(j, 3 - c))
                return false;
		
		else if (color[j] == color[x])
            return false;
	
	return true;

void solve()

    cin>>n>>m;
    for(int i=0;i<=n+m;i++) e[i].clear(),color[i]=0;
    int id=n;
    for(int i=1;i<=m;i++)
    
        int u,v;string s;
        cin>>u>>v>>s;
        if(s=="crewmate")
        
            int g=++id; //建立虚点
            e[u].push_back(g), e[g].push_back(u);
            e[g].push_back(v), e[v].push_back(g);
        
        else
            e[u].push_back(v), e[v].push_back(u);
    
    int flag=1;
    int ans=0;
    for(int i=1;i<=id;i++)
    
        if(!color[i])
        
            mx1=mx2=0;
            if(!dfs(i,1))
            
                cout<<-1<<endl;
                return;
            
            ans+=max(mx1,mx2);
        
    
    cout<<ans<<endl;

signed main()

    ios;
    int t;cin>>t;
    while(t--)
    
        solve();
    
    return 0;


D. The Door Problem

思路:
1.对于一扇门来说,如果本来就是开的,那么控制改灯的两个开关应属于不用阵营;
2.若一扇门本来是关的,那么控制改灯的两个开关应属于统一阵营

#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=700010;
const int inf=0x3f3f3f3f;
int n,m,a[N];
int color[N],mx1,mx2;
vector<int>q[N];
struct node

    int to,nxt,w;
e[N];
int cnt,head[N];
void add(int from,int to,int w)

    e[++cnt].to=to;
    e[cnt].w=w;
    e[cnt].nxt=head[from];
    head[from]=cnt;

bool dfs(int x, int c)

    if(color[x]) 
        return color[x]==c;
	color[x] = c;
	for (int i=head[x];i;i=e[i].nxt)
	
	    int j=e[i].to;
        if(!dfs(j, e[i].w?c:(3-c)))
            return 0;
	
	return 1;

void solve()

    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=m;i++)
    
        int sz;cin>>sz;
        for(int j=1;j<=sz;j++)
        
            int g;cin>>g;
            if(q[g].size()>0)
            
                add(i,q[g][0],a[g]);
                add(q[g][0],i,a[g]);
            
            else
                q[g].push_back(i);
        
    
    for(int i=1;i<=m;i++)
    
        if(!color[i])
        
            if(!dfs(i,1))
            
                cout<<"NO"<<endl;return;
            
        
    
    cout<<"YES"<<endl;

signed main()

    ios;
    solve();
    return 0;


最大完美匹配KM算法

https://www.bilibili.com/video/BV1cT411J7W5?spm_id_from=333.999.0.0&vd_source=91973ada1213cf6ba2cbb43a2bebd2e8
注释在代码中,详细则要需要观看大佬视频

#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=5e3+5;
const int inf=0x3f3f3f3f;
int match[N];       //右部点匹配了哪个左部点
int va[N],vb[N];    //标记是否在交替路中
int la[N],lb[N];    //左顶标、右顶标的值
int w[N][N],d[N];   //维护更新的delta值
int n;
bool dfs(int x)

    va[x]=1;
    for(int y=1;y<=n;y++)
    
        if(!vb[y])
        
            if(la[x]+lb[y]-w[x][y]==0)
            
                vb[y]=1;
                if(!match[y]||dfs(match[y]))
                
                    match[y]=x;return 1;
                
            
        
        else //不是相等子图则记录i最小的d[y]
            d[y]=min(d[y],la[x]+lb[y]-w[x][y]);
    
    return 0;

int KM()

    //预处理
    for(int i=1;i<=n;i++) la[i]=-inf;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        la[i]=max(la[i],w[i][j]);
    for(int i=1;i<=n;i++)
        lb[i]=0;
    for(int i=1;i<=n;i++)
    
        while(1)
        
            fill(va+1,va+n+1,0);
            fill(vb+1,vb+n+1,0);
            fill(d+1,d+n+1,inf);
            if(dfs(i)) break;
            int delta=inf;
            for(int j=1;j<=n;j++)
                if(!vb[j]) delta=min(delta,d[j]);
            for(int j=1;j<=n;j++)
            
                if(va[j]) la[j]-=delta;
                if(vb[j]) lb[j]-=delta;
            
        
    

void solve()

    memset(w,-inf,sizeof w);


signed main()

    ios;
    int t;cin>>t;
    while(t--)
    
        solve();
    
    return 0;


P2457 [SDOI2006]仓库管理员的烦恼

思路:
1.模板求的是最大完美匹配,而本题需要使用二分图KM算法,却求的是最小代价。
2.需要变型。左部点为货物,右部点为仓库。将第i种货物搬到第j个仓库所需代价,注意要减去该仓库本身就有的该类货物。
3.最小代码,将权值取反,跑最大完全匹配。再将最后结果取反。

代码:

#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=5e2+5;
const int inf=0x3f3f3f3f;
int match[N];       //右部点匹配了哪个左部点
int va[N],vb[N];    //标记是否在交替路中
int la[N],lb[N];    //左顶标、右顶标的值
int w[N][N],d[N];   //维护更新的delta值
int n,sum[N],mp[N]以上是关于8/9 基础思维+二分图染色+最大完美匹配KM算法的主要内容,如果未能解决你的问题,请参考以下文章

KM算法(二分图完美匹配)

KM算法带权二分图完美匹配

二分图最大权值匹配 KM算法 模板

HDU 3722 Card Game(二分图最佳完美匹配+KM算法)

UVa11383 二分图的最佳完美匹配

UVa11383 二分图的最佳完美匹配