二分图匹配
匈牙利算法
目前看到最清晰的题解
注:
匈牙利算法仅仅只针对左边的节点枚举,\(mat\) 存的是右边的节点对应左边的节点
\(vis\) 数组代表这一次增广路已经访问过的点,所以每次都要重置 \(vis\) 数组
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m,e;
vector<int>b[N];
int mat[N];
bool vis[N];
bool find(int x){
for(int v:b[x]){
if(vis[v]) continue;
vis[v]=1;//当前左节点是否被访问过
if(!mat[v]||find(mat[v])){//本质上mat仅存的是右节点对应的左节点
mat[v]=x;
return 1;
}
}
return 0;
}
int hungarian(int tot){
int cnt=0;
for(int i=1;i<=tot;i++){
memset(vis,0,sizeof(vis));
if(find(i)) cnt++;
}
return cnt;
}
int main(){
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=e;i++){
int u,v;
scanf("%d%d",&u,&v);
v+=n;
b[u].push_back(v);
b[v].push_back(u);
}
printf("%d\n",hungarian(n));
}
T1:
学了一大顿最小点覆盖怎么实现,但突然意识到,最小点覆盖不就等于最大匹配数吗,就求个值即可
T2:
调了一大顿
最大独立集等于点数-最小点覆盖
注:一般图最大独立集的方法还没找到,所以就考虑是不是二分图,发现可以用棋盘分割成二分图解决
注:匈牙利算法建边时只需要一侧点的边向另一侧连,所以这里只需要从黑点像白点建即可
感谢deepseek提供的trick:vis数组不用每次都清空,添加一个时间戳
如果90ptsTLE:建议从 \((i+j)%2==1\) 向外连边或许能过
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=40005,M=205;
const int dx[8]={-1,-2,+1,+2,-1,-2,+1,+2};
const int dy[8]={-2,-1,-2,-1,+2,+1,+2,+1};
int n,k,ans,t;
int mat[N],a[N],g[M][M],vis[N];
char c[N];
vector<int>b[N];
bool find(int x){
if(!x) return 0;
for(int v:b[x]){
if(vis[v]==t||!v) continue;
vis[v]=t;
if(!mat[v]||find(mat[v])){
mat[v]=x;
return 1;
}
}
return 0;
}
int id(int i,int j){
if(i<1||i>n||j<1||j>n||g[i][j]) return 0;
return (i-1)*n+j;
}
int hungarian(int tot){
int cnt=0;
for(int i=1;i<=tot;i++){
for(int j=1;j<=tot;j++){
if((i+j)%2==1&&id(i,j)){
t++;
if(find(id(i,j))) cnt++;
}
}
}
return cnt;
}
void add(int i,int j,int c){
if(c==1) return;
ans++;
for(int de=0;de<8&&(i+j)%2==1;de++){
int nx=i+dx[de],ny=j+dy[de],sx=i,sy=j;
if(id(nx,ny)) b[id(sx,sy)].push_back(id(nx,ny));
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",c);
for(int j=1;j<=n;j++){
g[i][j]=c[j-1]-'0';
//add(i,j,c[j-1]-'0');
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
add(i,j,g[i][j]);//应当在这里加边,因为我们边的端点不能有障碍物,而前面加边不知道障碍物在哪
}
}
printf("%d",ans-hungarian(n));
}
最小点覆盖问题
建议浏览这个
P2764
板子,好水一道紫(
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=505;
int n,m,t,cnt;
int vis[N],mat[N],h[N],to[N];
vector<int>b[N],ans[N];
bool find(int x){
for(int v:b[x]){
if(vis[v]==t) continue;
vis[v]=t;
if(!mat[v]||find(mat[v])){
mat[v]=x;
to[x]=v;
return 1;
}
}
return 0;
}
int hungarian(int tot){
int cnt=0;
for(int i=1;i<=tot;i++){
t++;
if(find(i)) cnt++,h[i]=1;
}
return cnt;
}
void dfs(int x,int g){
if(mat[x]) dfs(mat[x]+n,g);
ans[g].push_back(x-n);
}
void sta(){
for(int i=1;i<=n;i++){
if(!h[i]){
dfs(i+n,i);
for(int j:ans[i]){
printf("%d ",j);
}
printf("\n");
cnt++;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
b[u].push_back(v+n);
}
hungarian(n);
sta();
printf("%d\n",cnt);
}
有向图的最小路径覆盖
可以看这篇
T3:
模板题
点击查看代码
#include<bits/stdc++.h>
#define sx(x) dot[x].sx
#define sy(x) dot[x].sy
#define tx(x) dot[x].tx
#define ty(x) dot[x].ty
#define tim(x) dot[x].tim
using namespace std;
const int N=505;
int n,m,t,T;
int vis[N],mat[N];
struct taxi{
int sx,sy,tx,ty,tim;
}dot[N];
vector<int>b[N];
bool find(int x){
for(int v:b[x]){
if(vis[v]==t) continue;
vis[v]=t;
if(!mat[v]||find(mat[v])){
mat[v]=x;
return 1;
}
}
return 0;
}
int hungarian(int tot){
int cnt=0;
for(int i=1;i<=tot;i++){
t++;
if(find(i)) cnt++;
}
return cnt;
}
int minit(char time[6]){
int h=(time[0]-'0')*10+(time[1]-'0');
int mi=(time[3]-'0')*10+(time[4]-'0');
return h*60+mi;
}
int len(int a,int b,int c,int d){
return abs(a-c)+abs(b-d);
}
void add(int i,int j){
if(tim(i)>tim(j)) swap(i,j);
int l=len(tx(i),ty(i),sx(j),sy(j))+len(sx(i),sy(i),tx(i),ty(i));
if(tim(i)+l<tim(j)) b[i].push_back(j+n);//,printf("%d %d %d\n",tim(i),l,tim(j))
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&m);
for(int i=1;i<=m;i++){
char tim[6];
scanf("%s",tim);
tim(i)=minit(tim);
scanf("%d%d%d%d",&sx(i),&sy(i),&tx(i),&ty(i));
}
for(int i=1;i<=m;i++){
for(int j=i+1;j<=m;j++){
add(i,j);
}
}
printf("%d\n",m-hungarian(m));
t=0;
for(int i=1;i<=m;i++){
vis[i]=mat[i]=0;
b[i].clear();
}
}
}

浙公网安备 33010602011771号