能力提升綜合題單 Part 8.8 二分圖

1.P3386 【模板】二分圖最大匹配

匈牙利算法, 常數小,複雜度沒有網絡流優秀,但是其實小數據跑的更快

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+5;
int n,m,k;
vector<int>e[maxn];
int match[maxn],vis[maxn];
bool dfs(int u){
	int len=e[u].size();
	for(int i=0;i<len;i++){
		int v=e[u][i];
		if(vis[v])continue;
		vis[v]=1;
		if(!match[v]||dfs(match[v])){
			match[v]=u;
			match[u]=v;
			return true;
		}
	}
	return false;
}
int xyl(){
	int ans=0;
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		if(dfs(i))ans++;
	}
	return ans;
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=k;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		if(a>n||b>m)continue;
		e[a].push_back(n+b);
		e[n+b].push_back(a);
	}
	printf("%d",xyl());
	return 0;
}

2.P2756 飛行員配對方案問題

裸的匈牙利,因爲自帶記錄匹配,直接輸出即可

#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=2e3+5;
int n,m,k;
int e[maxn][maxn];
int match[maxn],vis[maxn];
bool dfs(int u){
	for(int v=m+1;v<=n;v++){
		if(vis[v]||!e[u][v])continue;
		vis[v]=1;
		if(!match[v]||dfs(match[v])){// 如果v沒有匹配,或者v的匹配找到了新的匹配
			match[v]=u;
			match[u]=v;          // 更新匹配信息
			return true;
		}
	}
	return false;
}
int xyl(){
	int ans=0;
	memset(match,0,sizeof(match));
	for(int i=1;i<=n;i++){// 對每一個點嘗試匹配
		memset(vis,0,sizeof(vis));
		if(dfs(i))ans++;
	}
	return ans;
}
inline void init(){
	memset(e,0,sizeof(e));
}
int main(){
	scanf("%d%d",&m,&n);
	int u,v;
	while(scanf("%d%d",&u,&v)){
		if(u==-1&&v==-1)break;
		e[u][v]=1;
		e[v][u]=1;
	}
	printf("%d\n",xyl());
	for(int i=1;i<=m;i++){
		if(match[i]){
			printf("%d %d\n",i,match[i]);
		}
	}
	return 0;
}

3.P1129 [ZJOI2007]矩陣遊戲

行列匹配數是否==行數,可以只考慮一種移法,只移動行或者列,然後只要每個行列匹配成功,更換他們的位置並不會影響匹配數

#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
int n,m,k;
int e[maxn][maxn];
int match[maxn],vis[maxn],a[maxn][maxn];
bool dfs(int u){
	for(int v=1;v<=n;v++){
		if(vis[v]||!e[u][v])continue;
		vis[v]=1;
		if(!match[v]||dfs(match[v])){
			match[v]=u;
			return true;
		}
	}
	return false;
} 
int xyl(){
	int ans=0;
	memset(match,0,sizeof match);
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof vis);
		if(dfs(i))ans++;
	}
	return ans;
}
inline void init(){
	memset(e,0,sizeof e);
}
int main(){
	int tt;
	cin>>tt;
	while(tt--){
		cin>>n;
		init();
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				scanf("%d",&e[i][j]);
			}
		}
		int ans=xyl();
		if(ans==n)printf("Yes\n");
		else printf("No\n");
	}
}

4.P1559 運動員最佳匹配問題

裸的KM

#include<bits/stdc++.h>
using namespace std;
#define int long long
const long long maxn=1005,inf=9e18;
int n,m,mp[maxn][maxn],matched[maxn],p[105][105],q[105][105];
int slack[maxn],ex[maxn],ey[maxn],pre[maxn];
int visx[maxn],visy[maxn];
void match(int u){
	int x,y=0,yy=0,d;
	memset(pre,0,sizeof(pre));
	for(int i=1;i<=n;i++)slack[i]=inf;
    matched[y]=u;
	while(1){
		x=matched[y];
		d=inf;
		visy[y]=1;
		for(int i=1;i<=n;i++){
			if(visy[i])continue;
			if(slack[i]>ex[x]+ey[i]-mp[x][i]){
				slack[i]=ex[x]+ey[i]-mp[x][i];
				pre[i]=y;
			}
			if(slack[i]<d){
				d=slack[i];
				yy=i;
			}
		}
		for(int i=0;i<=n;i++){
			if(visy[i])ex[matched[i]]-=d,ey[i]+=d;
			else slack[i]-=d;
		}
		y=yy;
		if(matched[y]==-1)break;
	}
	while(y){
		matched[y]=matched[pre[y]];
		y=pre[y];
	}
}
int km(){
	memset(matched,-1,sizeof(matched));
	memset(ex,0,sizeof(ex));
	memset(ey,0,sizeof(ey));
	for(int i=1;i<=n;i++){
		memset(visy,0,sizeof(visy));
		match(i);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(matched[i]!=-1)ans+=mp[matched[i]][i];
	}
	return ans;
}
signed main()
{	
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            mp[i][j]=-inf;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		cin>>p[i][j];
		}
	}
	for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		cin>>q[i][j];
		}
	}
	for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		mp[i][j]=p[i][j]*q[j][i];
		}
	}
  //  for(int i=1;i<=m;i++)
  //  {	
    //    int u,v,w;
   //     scanf("%lld%lld%lld",&u,&v,&w);
  //      mp[u][v]=w;
 //   }
    printf("%lld\n",km());
    for(int i=1;i<=n;i++){
    //	printf("%lld ",matched[i]);
	}
    return 0;
}

5.P2423 [HEOI2012]朋友圈

分析一下AB國的情況:
A國只能一奇一偶,所以這個國的團中最多隻有1個或者2個人

B國奇偶相同的在同一個團

所以可以枚舉a國的每一個團,1或者2,跟b國進行匹配

最大團=補圖最大獨立集=總點數-補圖最大匹配數

不能無腦memset而要用時間戳進行優化匈牙利,否則會T

(最後這題寫着多組輸入但是其實並沒有,數據有鍋)

#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
int n,m,k,cnt,ans,num,t,flag[maxn];
int e[maxn][maxn],head[maxn],a[maxn],b[maxn];
int match[maxn],vis[maxn];
struct edge{
	int v,nex;
}ee[2000*2000+5];
inline void add(int u,int v){
	ee[++cnt].v=v;
	ee[cnt].nex=head[u];
	head[u]=cnt;
}
bool dfs(int u){
	for(int i=head[u];i;i=ee[i].nex){
		int v=ee[i].v;
		if(vis[v]!=num&&flag[v]==t){
			vis[v]=num;
			if(!match[v]||dfs(match[v])){
				match[v]=u;
				return true;
			}
		}
	}
	return false;
}
int main(){
	int tt,A,B;
	cin>>tt;
	while(tt--){
		ans=0;
		cin>>A>>B>>m;
		for(int i=1;i<=A;i++){
			scanf("%d",&a[i]);
		}
		for(int i=1;i<=B;i++){
			scanf("%d",&b[i]);
		}
		for(int i=1;i<=B;i++){
			if(b[i]&1){
				for(int j=1;j<=B;j++){
					if(!(b[j]&1)&&!((__builtin_popcount((b[i]|b[j])))&1)){
						add(i,j);
					}
				}
			}
		}
		int u,v;
		for(int i=1;i<=m;i++){
			scanf("%d%d",&u,&v);
			e[u][v+A]=1;
			e[v+A][u]=1;
		}
		for(int i=1;i<=B;i++){
			if((b[i]&1)){
				num++;
				if(dfs(i))ans++;
			}
		}
		ans=B-ans;
		for(int i=1;i<=A;i++){

				t++;
		    	int sum=0,now=0;
		    	memset(match,0,sizeof match);
		    	for(int j=1;j<=B;j++){
		    		if(e[i][j+A]){
		    			flag[j]=t;
		    			now++;
		    		}
		    	}
		    	for(int j=1;j<=B;j++){
		    		if(flag[j]==t&&(b[j]&1)){
		    			num++;
		    			if(dfs(j))sum++;
		    		}
		    	}
		    	ans=max(ans,now-sum+1);

		}
		for(int i=1;i<=A;i++){
			for(int j=i+1;j<=A;j++){
				if((a[i]^a[j])&1){
					t++;
			    	int sum=0,now=0;
			    	memset(match,0,sizeof match);
			    	for(int k=1;k<=B;k++){
			    		if(e[i][A+k]&&e[j][A+k]){
			    			flag[k]=t;
			    			now++;
			    		}
			    	}
			    	for(int k=1;k<=B;k++){
			    		if(flag[k]==t&&(b[k]&1)){
			    			num++;
			    			if(dfs(k))sum++;
						}
			    	}
			    	ans=max(ans,now-sum+2);
				}
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

6.P2764 最小路徑覆蓋問題

給定有向圖 G=(V,E)G=(V,E) 。設 PPGG 的一個簡單路(頂點不相交)的集合。如果 VV 中每個定點恰好在PP的一條路上,則稱 PPGG 的一個路徑覆蓋。PP中路徑可以從 VV 的任何一個定點開始,長度也是任意的,特別地,可以爲 00GG 的最小路徑覆蓋是 GG 所含路徑條數最少的路徑覆蓋。設計一個有效算法求一個 GAP (有向無環圖) GG 的最小路徑覆蓋。

提示:設 V={1,2,...,n}V=\{1,2,...,n\} ,構造網絡 G1={V1,E1}G_1=\{V_1,E_1\} 如下:

V1={x0,x1,...,xn}{y0,y1,...,yn}V_1=\{x_0,x_1,...,x_n\}\cup\{y_0,y_1,...,y_n\}

E1={(x0,xi):iV}{(yi,y0):iV}{(xi,yj):(i,j)E}E_1=\{(x_0,x_i):i\in V\}\cup\{(y_i,y_0):i\in V\}\cup\{(x_i,y_j):(i,j)\in E\}

每條邊的容量均爲 11 ,求網絡 G1G_1(x0,y0)(x_0,y_0) 最大流。

題目直接給出了提示,可是我太蒻了沒有看懂…

原圖是這樣子的:
在這裏插入圖片描述
要我們求最小覆蓋路徑,我們可以先把圖看成沒有邊,此時覆蓋路徑數爲11,通過手玩可以發現,每當我們連上一條邊之後,這個路徑數就會減少1,而每一條路徑上,一個點只能被另外一個點連上,這就符合匹配的原理,我們由此構建出二分圖
在這裏插入圖片描述
可以看出最小路徑覆蓋數=總點數-最大流,因爲最大流即爲匹配數,被匹配上的點就不需要做出發點

題目要求我們輸出路徑,在dfs找增廣路的時候記錄下每一個點u>vu->vvv就好

#include<bits/stdc++.h>
using namespace std;
const int inf =0x3f3f3f3f,maxn=1e5+5,maxm=4e5+5;
int cur[maxn],head[maxn],dep[maxn],to[maxn],flag[maxn];
int cnt=1,n,m,s,t;
struct edge{
	int v,w,nex;
}e[maxm*2];
inline void add_edge(int u,int v,int w){
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].nex=head[u];
	head[u]=cnt;
	e[++cnt].v=u;
	e[cnt].w=0;
	e[cnt].nex=head[v];
	head[v]=cnt;
}
bool bfs(){
	memset(dep,0,sizeof(dep));
    for(int i=0;i<=2*n+5;i++){//如果要拆點或者其他的一定記得把範圍開大點!!! 
    	cur[i]=head[i];
	}
	dep[s]=1;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=e[i].nex){
			int v=e[i].v;
			if(!dep[v]&&e[i].w){
				dep[v]=dep[u]+1;
				q.push(v);
				if(v==t)return true;		
			}
		}
	}
	return false;
}
int dfs(int u,int now){
	if(u==t||now==0){
		return now;
	}
	int flow=0,rlow=0;
	for(int i=cur[u];i;i=e[i].nex){
		int v=e[i].v;
		if(e[i].w&&dep[v]==dep[u]+1){
			if(rlow=dfs(v,min(now,e[i].w))){
				flow+=rlow;
				now-=rlow;
				e[i].w-=rlow;
				e[i^1].w+=rlow;
				to[u]=v;
				if(u!=s)flag[v-n]=1;
				if(now==0)return flow;
			}
		}
	}
	if(!flow)dep[u]=-1;
	return flow;
}
int dinic(){
	int maxflow=0; 
	while(bfs()){
		maxflow+=dfs(s,inf);
	}
	return maxflow;
}
int main(){
	scanf("%d%d",&n,&m);
	int u,v,w;
	s=0,t=2*n+1;
	for(int i=1;i<=n;i++){
		add_edge(s,s+i,1);
		add_edge(s+n+i,t,1);
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		add_edge(s+u,s+n+v,1);
	}
	int ans=dinic();
	for(int i=1;i<=n;i++){
		if(!flag[i]){
			int now=i;
			printf("%d ",now);
			while(to[now]&&to[now]!=t){
				printf("%d ",to[now]-n);
				now=to[now]-n;
			}
			cout<<endl;
		}
	}
	printf("%d",n-ans);
	return 0;
}

7.P2825 [HEOI2016/TJOI2016]遊戲

每隔一個牆就cnt++cnt++,對行列進行匹配求出最大匹配數即可

#include<bits/stdc++.h>
using namespace std;
const int maxn=2005+5;
int xcnt=1,ycnt=1;
int n,m,k;
int e[maxn][maxn],x[maxn][maxn],y[maxn][maxn];
int match[maxn],vis[maxn];
char a[maxn][maxn];
bool dfs(int u){
	for(int v=1;v<=xcnt;v++){
		if(vis[v]||!e[u][v])continue;
		vis[v]=1;
		if(!match[v]||dfs(match[v])){
			match[v]=u;
			return true;
		}
	}
	return false;
} 
int xyl(){
	int ans=0;
	memset(match,0,sizeof match);
	for(int i=1;i<=xcnt;i++){
		memset(vis,0,sizeof vis);
		if(dfs(i))ans++;
	}
	return ans;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]=='*'){
				x[i][j]=xcnt;
			}
			else if(a[i][j]=='#')xcnt++;
		}
		xcnt++;
	}
	for(int j=1;j<=m;j++){
		for(int i=1;i<=n;i++){
			if(a[i][j]=='*'){
				y[i][j]=ycnt;
			}
			else if(a[i][j]=='#')ycnt++;
		}
		ycnt++;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			int u=x[i][j];
			int v=y[i][j];
			if(a[i][j]=='*'){
				e[u][v]=1;
			}
		}
	}
	printf("%d",xyl());
	return 0;
}

8.P3033 [USACO11NOV]Cow Steeplechase G

剛開始讀錯了題,後面突然發現是要求最大團,秒殺之

再寫一遍:最大團=補圖最大獨立集=總點數-補圖最大匹配數

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int n,m,k,xcnt,ycnt;
int e[maxn][maxn];
int match[maxn],vis[maxn],a[maxn][maxn];
struct xd{
	int x,y1,y2;
}xd[maxn];
struct yd{
	int x1,x2,y;
}yd[maxn];
bool dfs(int u){
	for(int v=1;v<=n;v++){
		if(vis[v]||!e[u][v])continue;
		vis[v]=1;
		if(!match[v]||dfs(match[v])){
			match[v]=u;
			return true;
		}
	}
	return false;
} 
int xyl(){
	int ans=0;
	memset(match,0,sizeof match);
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof vis);
		if(dfs(i))ans++;
	}
	return ans;
}
int main(){
	cin>>n;
	int x1,x2,y1,y2;
	for(int i=1;i<=n;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		if(x1==x2){
			xd[++xcnt].x=x1;
			xd[xcnt].y1=y1;
			xd[xcnt].y2=y2;
		}
		else if(y1==y2){
			yd[++ycnt].y=y1;
			yd[ycnt].x1=x1;
			yd[ycnt].x2=x2;
		}
	}
	for(int i=1;i<=xcnt;i++){
		int x=xd[i].x;
		int y1=xd[i].y1;
		int y2=xd[i].y2;
		if(y1>y2)swap(y1,y2);
		for(int j=1;j<=ycnt;j++){
			int y=yd[j].y;
			int x1=yd[j].x1;
			int x2=yd[j].x2;
			if(x1>x2)swap(x1,x2);
			if(x1<=x&&x2>=x&&y1<=y&&y2>=y){
				e[i][j]=1;
			}
		//	e[i][j]=1;
		}
	}
	printf("%d",max(xcnt,max(ycnt,n-xyl())));
	return 0;
}

9.P3731 [HAOI2017]新型城市化

這個題做的我晚上腦子快要爆炸…

一看數據範圍很明顯是要nlognnlogn的做法纔行

半天也沒看明白題目的真意…

題目給出了反圖

題目問加上哪些邊可以讓最大團變大

最大團=總點數-補圖最大匹配數

所以補圖的最大匹配數越小,最大團就越大,也就是補圖最大獨立集越大,也就是刪掉哪些邊能讓圖的最大獨立集增大

luogu上的dalao的分析:
考慮如下定理:若一條邊一定在最大匹配中,則在最終的殘量網絡中,這條邊一定滿流,且這條邊的兩個頂點一定不在同一個強連通分量中。

證明也很簡單:首先滿流的要求是很顯然的,其次,如果這兩個點在同一個強連通分量中,那麼一定有一個環經過這條邊,沿着環增廣一下,網絡仍然滿足流量限制,但是這條邊就不滿流了,於是就得到了一組新的最大匹配。

於是我們終於讀懂題目了,就是求二分圖匹配的必須邊

直接在殘量網絡裏跑tarjantarjan,就是如果這條邊沒有滿流就連上

對於一條邊(x,y)(x,y)

如果(x,y)(x,y)是一條匹配邊或者xxyy在同一個強聯通分量裏,那麼這就是一條最大匹配的可行邊

如果(x,y)(x,y)是一條匹配邊並且xxyy不在同一個強連通分量裏,那麼這就是一條必須邊

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+5;
const int maxm=150000+5,inf=0x3f3f3f3f;
int head[maxn],n,m,cnt=1,dep[maxn],vis[maxn],cur[maxn];
int s,t,co[maxn];
struct edge{
	int v,w,nex;
}e[maxm*2];
vector<int>ee[maxn];
inline void add_edge(int u,int v,int w){
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].nex=head[u];
	head[u]=cnt;
	e[++cnt].v=u;
	e[cnt].w=0;
	e[cnt].nex=head[v];
	head[v]=cnt;
}
bool bfs(){
	memset(dep,0,sizeof(dep));
    for(int i=s;i<=t;i++){//如果要拆點或者其他的一定記得把範圍開大點!!! 
    	cur[i]=head[i];
	}
	dep[s]=1;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=e[i].nex){
			int v=e[i].v;
			if(!dep[v]&&e[i].w){
				dep[v]=dep[u]+1;
				q.push(v);
				if(v==t)return true;		
			}
		}
	}
	return false;
}
int dfs(int u,int now){
	if(u==t||now==0){
		return now;
	}
	int flow=0,rlow=0;
	for(int i=cur[u];i;i=e[i].nex){
		int v=e[i].v;
		if(e[i].w&&dep[v]==dep[u]+1){
			if(rlow=dfs(v,min(now,e[i].w))){
				flow+=rlow;
				now-=rlow;
				e[i].w-=rlow;
				e[i^1].w+=rlow;
				if(now==0)return flow;
			}
		}
	}
	if(!flow)dep[u]=-1;
	return flow;
}
int dinic(){
	int maxflow=0;
	while(bfs()){
		maxflow+=dfs(s,inf);
	}
	return maxflow;
}
namespace Tarjan {
	std::vector<int> v[maxn];
	int dfn[maxn],low[maxn],col[maxn],st[maxn],f[maxn];
	int top,p,cnt,mid,tot;
	std::pair<int,int> ans[150005];
	void tarjan(int x) {
		dfn[x]=low[x]=++cnt;
		f[x]=1,st[++top]=x;
		for(int i=0;i<v[x].size();++i) {
			int j=v[x][i];
			if(!dfn[j]) tarjan(j),low[x]=min(low[x],low[j]);
			else if(f[j]) low[x]=min(low[x],dfn[j]);
		}
		if(dfn[x]==low[x]) {
			++p;
			do {
				mid=st[top--];
				f[mid]=0;
				col[mid]=p;
			}while(mid!=x);
		}
	}
	void solve() {
		for(int i=s;i<=t;i++)
			for(int j=head[i];j;j=e[j].nex)
			if(e[j].w) v[i].push_back(e[j].v);
		for(int i=s;i<=t;i++) if(!dfn[i]) tarjan(i);
		for(int i=1;i<=n;i++) {
			if(co[i]) continue;
			for(int j=head[i];j;j=e[j].nex)
			if(col[i]!=col[e[j].v]&&!e[j].w&&e[j].v!=s) {
				int xx=min(i,e[j].v);
				int yy=max(i,e[j].v);
				ans[++tot]=make_pair(xx,yy);
			}
		}
		std::sort(ans+1,ans+tot+1);
		printf("%d\n",tot);
		for(int i=1;i<=tot;i++) printf("%d %d\n",ans[i].first,ans[i].second);
	}
}
void rs(int u,int c){
	co[u]=c;
	for(int i=0;i<ee[u].size();i++) {
		int v=ee[u][i];
		if(co[v]!=2) continue;
		rs(v,c^1);
	}
}
int main(){
	cin>>n>>m;
	int x,y;
	t=n+1;
	s=0;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		ee[x].push_back(y);
		ee[y].push_back(x);
	}
	for(int i=1;i<=n;i++){
		co[i]=2;
	}
	for(int i=1;i<=n;i++){
		if(co[i]==2)rs(i,0);
	}
	for(int i=1;i<=n;i++){
		if(!co[i])add_edge(s,i,1);
		else add_edge(i,t,1);
	}
	for(int i=1;i<=n;i++){
		if(co[i])continue;
		for(int j=0;j<ee[i].size();j++){
			add_edge(i,ee[i][j],1);
		}
	}
	int an=dinic();
	Tarjan::solve();
	return 0;
}

10.P4014 分配問題

正負KM

#include<bits/stdc++.h>
using namespace std;
#define int long long
const long long maxn=1005,inf=1e18;
int n,m,mp[maxn][maxn],matched[maxn];
int slack[maxn],ex[maxn],ey[maxn],pre[maxn];
int visx[maxn],visy[maxn];
inline void init(){
	memset(slack,0,sizeof slack);
	memset(ex,0,sizeof ex);
	memset(ey,0,sizeof ey);
	memset(pre,0,sizeof pre);
	memset(matched,0,sizeof matched);
	memset(visx,0,sizeof visx);
	memset(visy,0,sizeof visy);
}
void match(int u){
	int x,y=0,yy=0,d;
	memset(pre,0,sizeof(pre));
	for(int i=1;i<=n;i++)slack[i]=inf;
    matched[y]=u;
	while(1){
		x=matched[y];
		d=inf;
		visy[y]=1;
		for(int i=1;i<=n;i++){
			if(visy[i])continue;
			if(slack[i]>ex[x]+ey[i]-mp[x][i]){
				slack[i]=ex[x]+ey[i]-mp[x][i];
				pre[i]=y;
			}
			if(slack[i]<d){
				d=slack[i];
				yy=i;
			}
		}
		for(int i=0;i<=n;i++){
			if(visy[i])ex[matched[i]]-=d,ey[i]+=d;
			else slack[i]-=d;
		}
		y=yy;
		if(matched[y]==-1)break;
	}
	while(y){
		matched[y]=matched[pre[y]];
		y=pre[y];
	}
}
int km(){
	memset(matched,-1,sizeof(matched));
	memset(ex,0,sizeof(ex));
	memset(ey,0,sizeof(ey));
	for(int i=1;i<=n;i++){
		memset(visy,0,sizeof(visy));
		match(i);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(matched[i]!=-1)ans+=mp[matched[i]][i];
	}
	return ans;
}
signed main(){	
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            mp[i][j]=-inf;
//    for(int i=1;i<=m;i++){	
 //       int u,v,w;
 //       scanf("%lld%lld%lld",&u,&v,&w);
 //       mp[u][v]=w;
 //   }
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		scanf("%lld",&mp[i][j]);
		}
	}
    int mm=km();
    init();
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		mp[i][j]=-mp[i][j];
		}
	}
	for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(mp[i][j]==-inf)mp[i][j]=inf;
	printf("%lld\n%lld",-km(),mm);
    return 0;
}

11.P4617 [COCI2017-2018#5] Planinarenje

黑題,咕咕咕待補

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章