[P1646][国家集训队]happiness(最小割)

建图,使源点为文,汇点为理,初始总喜悦度减可获得的最大喜悦度相当于图的最小割。

先从源点向每个人连流量为选文喜悦度的边,且从每个人向汇点连流量为选理喜悦度的边。然后对于没一组相邻的同学都单独建一个点,若是同选文,就从源点向该点连流量为额外喜悦度的边,且从该点向其所代表的两个人连流量为无线的边,选理道理相似。这样保证了只要任意一人不选该科,就不会获得额外喜悦度。然后跑最大流即可。

#include
#include
#include
#include
using namespace std;
const int N=50000,M=300000,inf=0x7fffffff;
namespace io{
	const int SIZE=(1<<21)+1;
	char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55];int qr;
	#define gc() (iT==iS?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iT==iS?EOF:*iS++):*iS++)
	inline void flush(){
		fwrite(obuf,1,oS-obuf,stdout);
		oS=obuf;
	}
	inline void putc(char x){
		*oS++ =x;
		if(oS==oT)flush();
	}
	template 
	inline void gi(I &x){
		for(c=gc();c<'0'||c>'9';c=gc());
		for(x=0;c>='0'&&c<='9';c=gc())x=x*10+(c&15);
	}
	template 
	inline void print(I &x){
		if(!x)putc('0');
		while(x)qu[++qr]=x%10+'0',x/=10;
		while(qr)putc(qu[qr--]);
	}
}
using io::gi;
using io::putc;
using io::print;
struct edge{
	int y,f,next;
}data[M];
int n,m,s,t,num,num1,tot,h[N],dep[N],cur[N];
queue q;
inline void addedge(int x,int y,int f){
	data[++num].y=y,data[num].f=f,data[num].next=h[x],h[x]=num;
	data[++num].y=x,data[num].f=0,data[num].next=h[y],h[y]=num;
}
inline int min1(int a,int b){
	return a0)dep[v]=dep[u]+1,q.push(v);
		}
	}
	return dep[t];
}
int dfs(int u,int t,int lim){
	if(u==t)return lim;
	int res=0;
	for(int &i=cur[u];i!=-1;i=data[i].next){
		int v=data[i].y;
		if(dep[v]==dep[u]+1&&data[i].f>0){
			int fmax=dfs(v,t,min1(lim-res,data[i].f));
			if(fmax){data[i].f-=fmax,data[i^1].f+=fmax,res+=fmax;if(res==lim)return res;}
		}
	}
	return res;
}
inline int dinic(){
	int ans=0;
	while(bfs())ans+=dfs(s,t,inf);
	return ans;
}
int main(){
	gi(n),gi(m);s=0,t=1,tot=0;
	memset(h,-1,sizeof h),num=-1;
	for(int y=1,i=1;i<=n;++i)for(int x,j=1;j<=m;++j)gi(x),++y,tot+=x,addedge(s,y,x);
	for(int y=1,i=1;i<=n;++i)for(int x,j=1;j<=m;++j)gi(x),++y,tot+=x,addedge(y,t,x);
	num1=n*m+1;
	for(int i=1;i

 

你可能感兴趣的:(最小割)