codeforces 1198E Rectangle Painting 2 网络流最小割求最小点权覆盖

codeforces 1198E Rectangle Painting 2

写在前面:

下面通过这道题顺便讲一下二分图中最小割求最小点权覆盖的合法性。(如果讲的有问题,欢迎评论区吐槽)这个东西在网络流中有所应用。

Part 1

由于一次喷漆的代价取决于长度和宽度中短的那个。那么最优的喷漆方案一定是一次喷一列或者一行。

(先假设黑点很少)于是如果一个点被覆盖,要么是它所在的列被喷了,要么是它所在的行被喷了。于是将它所在的行向它所在的列连一条边。连完之后由于行和列是两类东西,故分开放在两边,就得到了一张二分图。

由于每个黑点都要被喷漆,那么每条边的两个端点至少有一个被选中,于是问题就变成了最小点权覆盖。

但实际黑点很多,但都是一块一块的。我们可以一块黑点看成一个黑点,也就是离散化。就可以解决了。

那么离散化之后的一行可能就对应原图中的好多行,于是在选择这行可能就有了不等于1的代价,也就是有了点权不为1了。

最小点权覆盖:二分图中取一些点覆盖所有的边,且这些取的点的点权和在所有方案中最小。

Part 2

接下来讲一下如何用最小割解决最小点权覆盖。

先将其转化成网络流模型:从源点向每个对应行的点连一条流量为其点权的边,从每个对应列的点向汇点连一条流量为其点权的边,然后原边的流量都为无穷。

对于每个对应行的点,我们切断它到源点的边就相当于取这个点到最后的点集中,对于每个对应列的点,我们切断它到汇点的边就相当于取这个点到最后的点集中。

合法性:

如果最后的图中s和t是连通的,那么肯定存在一条边,它的左端点与s连通,它的右端点与t连通,那么这条边的两个端点都没被取到。是不合法的。如果s和t不连通,就不会出现上述情况了。

AC代码:

#include
#include
#include
#include
#define M 105
#define INF 1000000000
using namespace std;
struct Point{
	int Lx,Ly,Rx,Ry;
	void Read(){scanf("%d%d%d%d",&Lx,&Ly,&Rx,&Ry);}
}P[M];
int n,m;
int szx,szy;
int X[M<<1],Y[M<<1];
int Getx(int x){return lower_bound(X+1,X+szx+1,x)-X;}
int Gety(int x){return lower_bound(Y+1,Y+szy+1,x)-Y;}
void Init(){//离散化 
	for(int i=1;i<=m;i++)X[++szx]=P[i].Lx,X[++szx]=P[i].Rx+1,Y[++szy]=P[i].Ly,Y[++szy]=P[i].Ry+1;
	X[++szx]=1;X[++szx]=n+1;
	Y[++szy]=1;Y[++szy]=n+1;
	sort(X+1,X+szx+1);
	sort(Y+1,Y+szy+1);
	szx=unique(X+1,X+szx+1)-X-1;
	szy=unique(Y+1,Y+szy+1)-Y-1;
	for(int i=1;i<=m;i++)P[i].Lx=Getx(P[i].Lx),P[i].Rx=Getx(P[i].Rx+1),P[i].Ly=Gety(P[i].Ly),P[i].Ry=Gety(P[i].Ry+1);
}
struct E{
	int to,nx,d;
}edge[M*M*20];
int tot,head[M<<2];
void Addedge(int a,int b,int d){
	edge[++tot]=(E){b,head[a],d};
	head[a]=tot;
}
int s,t;
queue<int>Q;
int dep[M<<2],cur[M<<2];
bool bfs(){
	memset(dep,63,sizeof(dep));
	Q.push(s);dep[s]=0;
	for(int i=1;i<=szx+szy;i++)cur[i]=head[i];
	while(!Q.empty()){
		int now=Q.front();Q.pop();
		for(int i=head[now];~i;i=edge[i].nx){
			int nxt=edge[i].to;
			if(!edge[i].d||dep[nxt]<INF)continue;
			dep[nxt]=dep[now]+1;
			Q.push(nxt);
		}
	}
	return dep[t]<INF;
}
int dfs(int now,int lim){
	if(now==t)return lim;
	int res=0;
	for(int i=cur[now];~i;i=edge[i].nx){
		cur[now]=i;
		int nxt=edge[i].to;
		if(!edge[i].d)continue;
		if(dep[nxt]==dep[now]+1){
			int tmp=dfs(nxt,min(edge[i].d,lim));
			res+=tmp;
			lim-=tmp;
			edge[i].d-=tmp;
			edge[i^1].d+=tmp;
			if(!lim)break;
		}
	}
	return res;
}
int network_flow(){
	int res=0;
	while(bfs())res+=dfs(s,INF);
	return res;
}
void Solve(){
	for(int step=1;step<=m;step++){
		for(int i=P[step].Lx;i<P[step].Rx;i++){
			for(int j=P[step].Ly;j<P[step].Ry;j++){
				Addedge(i,j+szx-1,INF);
				Addedge(j+szx-1,i,0);
			}
		}
	}
	s=szx+szy-1,t=szx+szy;
	for(int i=1;i<szx;i++){Addedge(s,i,X[i+1]-X[i]);Addedge(i,s,0);}
	for(int i=1;i<szy;i++){Addedge(i+szx-1,t,Y[i+1]-Y[i]);Addedge(t,i+szx-1,0);}
	printf("%d\n",network_flow());//最小割 
}
int main(){
	tot=-1;memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)P[i].Read();
	Init();
	Solve();
	return 0;
}

你可能感兴趣的:(网络流)