一个图是二分图,图中不存在奇数环,染色法
最优解越大,图中右半部分越是二分图
染色法求二分图,这些图不一定联通!!
没有必要每次cheak都建图,在染色中增加判断权重就好了
#include
using namespace std;
const int N=1e6+100;
int h[N],ne[N],e[N],w[N],idx,rh[N];
void add(int a,int b,int c)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=c;
h[a]=idx++;
}
int n,m;
int colur[N];
bool dfs(int mid,int u,int c)
{
colur[u]=c;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(j==u) continue;
if(w[i]>mid)
{
if(colur[j]==0)
if(!dfs(mid,j,-c))
return false;
if(colur[j]==c)
return false;
}
}
return true;
}
bool cheak(int mid)
{
memset(colur,0,sizeof colur);
for(int i=1;i<=n;i++)
if(colur[i]==0)
{
if(!dfs(mid,i,1))
return false;
}
return true;
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
int l=0,r=0x3f3f3f3f;
while(l<r)
{
int mid=l+r>>1;
if(cheak(mid)) r=mid;
else l=mid+1;
}
cout<<r<<endl;
return 0;
}
把所有可以放的格子连一条边,在图中跑匈牙利算法
主要是维护match匹配数组,st标记数组,不太需要真的连边
#include
using namespace std;
const int N=110;
typedef pair<int,int>pll;
pll match[N][N];
int g[N][N];
int n,m;
int st[N][N];
int dx[4]={
-1,0,1,0},dy[4]={
0,1,0,-1};
bool find(int a,int b)
{
for(int i=0;i<4;i++)
{
int na=a+dx[i];
int nb=b+dy[i];
if(g[na][nb]) continue;
if(st[na][nb]) continue;
st[na][nb]=1;
if(na<1||na>n||nb<1||nb>n) continue;
if(match[na][nb].first==0 || (find(match[na][nb].first,match[na][nb].second)))
{
match[na][nb]={
a,b};
return true;
}
}
return false;
}
int main()
{
cin>>n>>m;
while(m--)
{
int a,b;
cin>>a>>b;
g[a][b]=1;
}
int res=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(g[i][j]==1) continue;
if(i+j&1)
{
memset(st,0,sizeof st);
if(find(i,j))
res++;
}
}
}
cout<<res<<endl;
return 0;
}
一条边都选一个点
在二分图中,最小覆盖数=最大匹配数
转化为求二分图的最小点覆盖
直接匈牙利算法
选择最多的点,保证这些点内部没有边。
在二分图中
最大独立集
= 去掉最少的点,把所有边破坏掉
= 去掉最小点覆盖
放置最多的骑士,保证互相不攻击到
奇偶二分图之后,选择最多的点,保证骑士之间没有边(独立起来)
= 去掉最少的点,这些点是最大点覆盖
#include
#include
#include
using namespace std;
const int N=200;
typedef pair<int,int>pll;
pll match[N][N];
int st[N][N],g[N][N];
int n,m;
int dx[8] = {
-2, -2, 2, 2, -1, -1, 1, 1};
int dy[8] = {
-1, 1, -1, 1, -2, 2, -2, 2};
bool find(int x,int y)
{
for(int i=0;i<8;i++)
{
int a=x+dx[i];
int b=y+dy[i];
if(a<1 || a> n|| b<1 || b>m) continue;
if(g[a][b])continue;
if(st[a][b]) continue;
st[a][b]=1;
if(match[a][b].first==-1 || find(match[a][b].first,match[a][b].second))
{
match[a][b]=make_pair(x,y);
return true;
}
}
return false;
}
int main()
{
int t;
cin>>n>>m>>t;
int tt=t;
while(t--)
{
int a,b;
cin>>a>>b;
g[a][b]=1;
}
int res=0;
memset(match,-1,sizeof match);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(g[i][j]==0 && (i+j)%2)
{
memset(st,0,sizeof st);
if(find(i,j))
res++;
}
}
}
cout<<n*m-tt-res<<endl;
return 0;
}
最少的互不相交路径,把所有点覆盖住
这个图是一个二分图,最少路径点覆盖=n-最大匹配数
先传递闭包,然后求最小路径点覆盖
#include
using namespace std;
const int N=500;
int match[N];
int st[N];
int g[N][N];
int n,m;
void floyd()
{
for (int k = 1; k <= n; k ++ )
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
g[i][j] |= g[i][k] & g[k][j];
}
bool find(int x)
{
for(int i=1;i<=n;i++)
{
if(st[i]) continue;
if(g[x][i]==0) continue;
st[i]=1;
if(!match[i] || find(match[i]))
{
match[i]=x;
return true;
}
}
return false;
}
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b;
cin>>a>>b;
g[a][b]=1;
//g[b][a]=1;
}
floyd();
int res=0;
for(int i=1;i<=n;i++)
{
memset(st,0,sizeof st);
if(find(i))
res++;
}
cout<<n-res<<endl;
}