8/9 基础思维+二分图染色+最大完美匹配KM算法
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8/9 基础思维+二分图染色+最大完美匹配KM算法相关的知识,希望对你有一定的参考价值。
二分图判定染色法
定理:二分图不存在奇环,只存在偶数环
#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算法的主要内容,如果未能解决你的问题,请参考以下文章