二分图,网络流与费用流

二分图

  1. 加超级源点与超级汇点,dinic做;

  2. 匈牙利算法

    vector<int> gra[maxn];
    void addedge(int u,int v){
    	gra[u].push_back(v);
    	gra[v].push_back(u);
    }
    int match[maxn];
    bool used[maxn];
    bool dfs(int u){
    	used[u]=true;
    	for(int i=0;i<(int)gra[u].size();i++){
    		int v=gra[u][i];
    		int w=match[v];
    		if(!w||!used[w]&&dfs(w)){
    			match[u]=v;
    			match[v]=u;
    			return true;
    		}
    	} 
    	return false;
    }
    int solve(){
    	int res=0;
    	memset(match,0,sizeof(match));
    	for(int i=1;i<=n;i++){
    		memset(used,0,sizeof(used));
    		if(dfs(i)) res++;
    	}
    	return res;
    }
    

poj3057

考虑(门d,时间T)这个二元组,对每个二元组,有确定的人可以恰巧在时间T通过门d(只要人离门的距离小于等于T即可),并且对这个确定的二元组只能通过一个人,对(二元组 | 可以通过的人)建二分图,按照T从小到大对二元组做匈牙利算法(dfs(i)),一旦匹配的边数等于人数,此时的T即为最小时间。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
char s[15][15];
const int maxn=6000;
vector<int> gra[maxn];
void addedge(int u,int v){
	gra[u].push_back(v);
	gra[v].push_back(u);
}
int match[maxn];
bool used[maxn];
bool dfs(int u){
	used[u]=true;
	for(int i=0;i<(int)gra[u].size();i++){
		int v=gra[u][i];
		int w=match[v];
		if(!w||!used[w]&&dfs(w)){
			match[u]=v;
			match[v]=u;
			return true;
		}
	} 
	return false;
}
int x,y;
int dis[150][150];
struct pos{
	int x,y;
	int d;
};
vector<pos> posd,posp;
int id[15][15];
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
bool inside(int a,int b){
	return a>=1&&a<=x&&b>=1&&b<=y;
}
int bfs(int S){
	bool vis[15][15]={0};
	queue<pos> que;
	posd[S].d=0;
	que.push(posd[S]);
	while(!que.empty()){
		pos u=que.front(); que.pop();
		for(int i=0;i<4;i++){
			int vx=u.x+dx[i],vy=u.y+dy[i];
			if(inside(vx,vy)&&s[vx][vy]=='.'&&dis[S][id[vx][vy]]==0x3f3f3f3f){
				dis[S][id[vx][vy]]=u.d+1;
				pos tmp;
				tmp.d=u.d+1; tmp.x=vx; tmp.y=vy;
				que.push(tmp);
			}
		}	
	}
}
void solve(){
	posd.clear(); posp.clear();
	for(int i=0;i<maxn;i++) gra[i].clear();
	cin >> x >> y;
	for(int i=1;i<=x;i++) cin >> s[i]+1;
	for(int i=1;i<=x;i++){
		for(int j=1;j<=y;j++){
			if(s[i][j]=='.'){
				pos tmp; tmp.x=i,tmp.y=j;
				posp.push_back(tmp);
				id[i][j]=posp.size()-1;
			}
			if(s[i][j]=='D'){
				pos tmp; tmp.x=i,tmp.y=j;
				posd.push_back(tmp);
				id[i][j]=posd.size()-1;
			}
		}
	}
	int np=posp.size(),nd=posd.size();
	if(!np){
		cout << 0 << endl; return;
	}
	memset(dis,0x3f3f3f3f,sizeof(dis));
	for(int i=0;i<nd;i++){
		bfs(i);
	}
	for(int T=1;T<=np;T++)
	for(int i=0;i<nd;i++){
		for(int j=0;j<np;j++){
			if(dis[i][j]<=T) addedge((T-1)*nd+i+1,np*nd+1+j);
		}
	}
	memset(match,0,sizeof(match));
	int num=0;
	for(int i=1;i<=np*nd;i++){
		memset(used,0,sizeof(used));
		if(dfs(i)){
			num++;
			if(num==np){
				cout << (i+nd-1)/nd << endl; return;
			}
		}
	}
	cout << "impossible" << endl;
}
int main(){
	int t;
	cin >> t;
	while(t--) solve();
}

poj3281

建图方式很经典。

poj3041

经典二分图。

二分图:最小点覆盖=最大匹配。

poj1486

枚举边,如若减去这条边以后最大匹配变小了,说明这条边是必须的。

洛谷p1963变换序列

关于最小字典序的处理。保证左边每个点的存边都是升序的。从后往前匹配。

最大字典序的话应该是左边每个点存边都是降序的,同样从后往前匹配。

洛谷p3231消毒

尚未理解。

最大流

FF算法+dinic优化+弧优化

理论复杂度O(VE),实际上更快。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn=300;
struct edge{
	int flow,to;
	int rev;
};
vector<edge> gra[maxn];
void addedge(int u,int v,int f){
	edge tmp;
	tmp.flow=f; tmp.to=v;
	gra[u].push_back(tmp);
	tmp.flow=0; tmp.to=u;
	gra[v].push_back(tmp);
	int sizu=gra[u].size(),sizv=gra[v].size();
	gra[u][sizu-1].rev=sizv-1;
	gra[v][sizv-1].rev=sizu-1;
}
int dep[maxn],s,t;
bool bfs(){
	memset(dep,0,sizeof(dep));
	dep[s]=1;
	queue<int> que;
	que.push(s);
	while(!que.empty()){
		int u=que.front();
		que.pop();
		int siz=gra[u].size();
		for(int i=0;i<siz;i++){
			edge v=gra[u][i];
			int vv=v.to;
			if(dep[vv]||!v.flow) continue;
			dep[vv]=dep[u]+1;
			que.push(vv);
		}
	}
	return dep[t];
}
int now[maxn];
int dfs(int cur,int flow){
	if(cur==t||!flow) return flow;
	int f;
	for(int i=now[cur];i<(int)gra[cur].size();i++){
		edge &v=gra[cur][i];
		int vv=v.to;
		if(dep[vv]==dep[cur]+1&&(f=dfs(vv,min(flow,v.flow)))){
			v.flow-=f;
			gra[vv][v.rev].flow+=f;
			return f;
		}
		now[cur]=i;
	}
	return 0;
}
int dinic(){
	int flow=0,f;
	while(bfs()){
		memset(now,0,sizeof(now));
		while(f=dfs(s,0x3f3f3f3f)) flow+=f;
	}
	return flow;
}

poj2112

弗洛伊德预处理最短路+二分+最大流判断。 水。

posted @ 2020-02-09 00:30  rain_star  阅读(11)  评论(0编辑  收藏  举报