二分答案
染色法二分图判定,其实就DFS
建图:怨气值>mid
的连边
注意开始DFS的时候不能DFS(1,0)
,因为0
没有col
#include
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar();
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1e5+5;
int n,m,col[N];
struct Data{
int p1,p2,val;
}a[N];
vector<int>G[N];
bool flag;
bool cmp(Data a,Data b){
return a.val<b.val;
}
void DFS(int u,int c){
if(!flag) return;
col[u]=c;
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(!col[v])
DFS(v,-c);
else if(col[v]==c)
flag=false;
}
return;
}
bool check(int pos){
for(int i=1;i<=n;++i) G[i].clear();
for(int i=pos+1;i<=m;++i){
G[a[i].p1].push_back(a[i].p2);
G[a[i].p2].push_back(a[i].p1);
}
flag=true;
memset(col,0,sizeof(col));
for(int i=1;i<=n;++i)
if(!col[i]){
DFS(i,1);
if(!flag) return false;
}
return true;
}
int main(){
n=in,m=in;
for(int i=1;i<=m;++i){
a[i].p1=in;
a[i].p2=in;
a[i].val=in;
}
if(m==1){
puts("0");
return 0;
}
sort(a+1,a+m+1,cmp);
int l=0,r=m,ans;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",a[ans].val);
return 0;
}
增广路的概念:
存在一条连接两个非匹配点的路径path,使得使得非匹配边与匹配边在path上交替出现,那么称path为增广路
注意,这里的一条指的不是一条边,而是多条边组成的路径
发掘一下性质:
由最后一条性质,可以扩大匹配边
即匈牙利算法,贪心,后来居上
放一点例题,傻逼洛谷上找不到题我就不做了
最重要的是如何建图
例 棋盘覆盖
n × m n\times m n×m棋盘上放骨牌( 1 × 2 1\times 2 1×2),有些格子禁止放,求最多放多少骨牌
n , m ≤ 100 n,m\leq 100 n,m≤100
DP可做,自己YY一下转移方程
考虑二分图:
每个骨牌就是一条边,连接两个格子
考虑建图:行号列号和为奇数、偶数的分别为二分图的左部和右部
最大匹配
Θ ( N 2 M 2 ) \Theta(N^2M^2) Θ(N2M2)
例 车的放置
还是一个棋盘 n × m n\times m n×m,放象棋车,互不攻击
有些位置禁止放置
n , m ≤ 200 n,m\leq 200 n,m≤200
其实递推是可以的(可参见福建省选的那道题)
考虑行号和列号为二分图左部右部
二分图匹配
Θ ( ( N + M ) × N M ) \Theta((N+M)\times NM) Θ((N+M)×NM)
完备匹配 左部右部节点数相同,且在最大匹配中均为匹配点
求到最大匹配判断一下就行了
多重匹配 就是求多个匹配
直接逃去网络流
多重匹配会有最有匹配,那贪心,二分就可以上来了
二分图能解决的一切问题网络流都可以解决
二分图不能解决的一切问题网络流还是都可以解决
但是有例外
有专门卡网络流的题让你写KM
KM算法见我博客
例 矩阵游戏
行列式的性质!!! 当我没说
一个黑点即连一条边
交换行列即交换同部节点
最后得到 ( 1 , 1 ) , ( 2 , 2 ) , ⋯ , ( n , n ) (1,1),(2,2),\cdots,(n,n) (1,1),(2,2),⋯,(n,n)的点就Yes
实际上交换同部节点不改变图的构成,因为这些节点只有个标号是起作用的
于是直接二分图匹配,匹配数 = n =n =n则Yes
注意建图,注意节点:
左部右部节点是有区别的!
如左部范围 [ 1 , n ] [1,n] [1,n],右部范围就不能是 [ 1 , n ] [1,n] [1,n],取 [ n + 1 , 2 n ] [n+1,2n] [n+1,2n]挺好
傻逼洛谷,印度女工又罢工了
#include
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar();
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=5e2+5;
int n,ans;
vector<int>G[N];
bool vis[N];
int mat[N];
bool DFS(int u){
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(!vis[v]){
vis[v]=true;
if(!mat[v]||DFS(mat[v])){
mat[v]=u;
return true;
}
}
}
return false;
}
int main(){
int T=in;
while(T--){
n=in,ans=0;
memset(mat,0,sizeof(mat));
for(int i=1;i<=n;++i) G[i].clear();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
int x=in;
if(x){
G[i].push_back(n+j);
G[n+j].push_back(i);
}
}
for(int i=1;i<=n;++i){
memset(vis,0,sizeof(vis));
ans+=DFS(i);
}
if(ans==n) puts("Yes");
else puts("No");
// for(int i=1;i<=n;++i){
// for(int e=0;e
// puts("");
// }
// printf("ans=%d\n",ans);
// for(int i=1;i<=n;++i) printf("%d ",mat[i]);puts("");
//
}
return 0;
}
例 变换序列
发现距离的含义可以把 T i T_i Ti扔到 [ N + 1 , 2 N ] [N+1,2N] [N+1,2N]上去
标准的二分图匹配
考虑字典序的问题
由于匈牙利算法的替换操作,我们没办法保证前面拿到字典序小的节点后面能保持这个值
考虑倒着寻找匹配,后面的点如果拿到了字典序小的点最后会被挤成字典序大的点
至于为什么不能仍然正向寻找匹配,最后倒着输出呢?
感觉上,发现二分图不是对称的,没办法倒着来~~(好玄学)~~
#include
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar();
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1e4+5;
int n,ans;
int G[2][N];
int vis[N],tim;
int mat[N],to[N];
bool DFS(int u){
for(int i=0;i<2;++i){
int v=G[i][u];
if(vis[v]!=tim){
vis[v]=tim;
if(mat[v]==-1||DFS(mat[v])){
mat[v]=u;
to[u]=v;
return true;
}
}
}
return false;
}
int main(){
memset(mat,-1,sizeof(mat));
n=in;
for(int i=0;i<n;++i){
int d=in;
int a=i-d<0?i-d+n:i-d;
int b=i+d>=n?i+d-n:i+d;
if(a>b) swap(a,b);
G[0][i]=a;
G[1][i]=b;
}
for(int i=n-1;~i;--i)
++tim,ans+=DFS(i);
if(ans<n) puts("No Answer");
else{
for(int i=0;i<n;++i)
printf("%d ",to[i]);
puts("");
}
return 0;
}
例 [ZJOI2009]假期的宿舍
好板
懒得写了(今天下午状态不好)