染色法(判断二分图)
对图进行dfs,①若子节点未染色,将其染成和根节点相反的颜色;②若子节点染色且和根节点相同,则判定不为二分图
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
const int N=1e5+5,M=N<<1;
int n,m;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int color[N];//-1未染色 0白色 1黑色
bool dfs(int u,int c){
color[u]=c;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(color[j]==-1){//子节点未染色
if(!dfs(j,!c)) return false;
}else if(color[j]==c){//子节点颜色与父节点相同,矛盾
return false;
}
}
return true;
}
bool check(){
for(int i=1;i<=n;i++){
if(color[i]==-1){
if(!dfs(i,0)) return false;
}
}
return true;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
memset(color,-1,sizeof color);
while(m--){
int a,b;cin>>a>>b;
add(a,b);add(b,a);
}
if(check()){
cout<<"Yes";
}else{
cout<<"No";
}
}
关押罪犯
两个罪犯之间有冲突值,将若干罪犯分到两个监狱,使得同一监狱的最大冲突值最小
二分枚举边权,将大于枚举边权的两点分到两边,求满足二分图的最小边权分界值
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+5,M=2e5+5;//注意双向边开两倍
int n,m;
int h[N],e[M],ne[M],w[M],idx;
void add(int a,int b,int c){
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
int color[N];
bool dfs(int u,int c,int mid){
color[u]=c;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(w[i]<=mid) continue;
if(color[j]==-1){
if(!dfs(j,!c,mid)) return false;
}else if(color[j]==c) return false;
}
return true;
}
bool check(int mid){
memset(color,-1,sizeof color);
for(int i=1;i<=n;i++){
if(color[i]==-1){
if(!dfs(i,0,mid)) return false;
}
}
return true;
}
int main(){
scanf("%d%d",&n,&m);
if(!m){
printf("0");
return 0;
}
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);add(b,a,c);
}
int l=0,r=1e9;
while(l<r){
int mid=l+r>>1;
if(check(mid)){
r=mid;
}else{
l=mid+1;
}
}
printf("%d",l);
}
匈牙利算法(二分图最大匹配)
表示 右边的点j 匹配 左边的点u
对左边的点遍历,对于一个点来说,考虑所有它连接的右边的点,如果右侧目标点未匹配或对应左侧点可以更换匹配的,那么匹配数++
完美牛棚
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
const int N=205*2,M=N*N;
int n,m;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int match[N];//match[j]=u 右边的点j 匹配 左边的点u
bool vis[N];
bool find(int u){
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(!vis[j]){
vis[j]=true;
if(match[j]==0||find(match[j])){//右侧目标点未匹配或对应左侧点可以更换匹配
match[j]=u;
return true;
}
}
}
return false;
}
int hungrey(){
int res=0;
for(int i=1;i<=n;i++){
memset(vis,false,sizeof vis);
if(find(i)) res++;
}
return res;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++){
int s;cin>>s;
while(s--){
int t;cin>>t;
add(i,t+n);//修改单间的编号,避免和牛编号重复
}
}
int res=hungrey();
cout<<res;
}
棋盘覆盖
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5,M=N*4;
int n;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
bool g[N][N];
int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};
int match[N];
vector<int> vc;
bool vis[N];
bool find(int u){
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(!vis[j]){
vis[j]=true;
if(match[j]==0||find(match[j])){
match[j]=u;
return true;
}
}
}
return false;
}
int hungrey(){
int res=0;
for(auto i:vc){
memset(vis,false,sizeof vis);
if(find(i)) res++;
}
return res;
}
int main(){
int t;
cin>>n>>t;
memset(h,-1,sizeof h);
while(t--){
int x,y;cin>>x>>y;
g[x][y]=true;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(g[i][j]) continue;
if((i+j)&1){
int pos=(i-1)*n+j;
vc.push_back(pos);
for(int k=0;k<4;k++){
int nx=i+dx[k],ny=j+dy[k];
if(nx<1||ny<1||nx>n||ny>n) continue;
if(g[nx][ny]) continue;
int npos=(nx-1)*n+ny;
add(pos,npos);
}
}
}
}
int res=hungrey();
cout<<res;
}