bzoj4823 cqoi2017 老C的方块【最大流】

看到题目其实感觉很麻烦,不知道从哪里入手,那么仔细观察所给出的有用信息...


我们考虑网格图是一个含有挡板的图,这个挡板的分布很有规律,大概是每一行的相邻两个挡板都隔了四个格子,并且奇数行的排列相同,偶数行的排列相同...


然后考虑不合法的方块形状有什么共同点:仔细观察就会发现,所有的不合法图形中,挡板的左边至少有一个格子,右边至少有一个格子,并且左边的格子连着一个格子,右边的的格子连着一个格子...也就是说,其实我们如果要使得整张图的所有方块构成的图形全部合法就要满足下图中如果挡板两边的紫色格子都有方块存放的话,那么,和这两个紫色格子相邻的色格子和黑色格子不能同时存在...


我们发现刚好相邻隔板之间的四个格子就是为不合法图案而设计的...

于是就变成了经典的限制问题...经典的最小割...

如果不考虑紫色的格子,那么这整张网格图就是一个二分图...我们给这张图染色...


那么对于所有的白点,我们连<S,x,w[x]>的边,对于所有的黑点我们连<x,T,w[x]>的边,然后因为要保证紫色格子周围黑白点不能同时存在,所以,对于所有的黑点,我们从紫色格子像黑点连$inf$的边,从白点像紫色格子连$inf$的边,然后因为我们两个紫色格子不同时存在的时候黑白点是可以同时存在的,所以两个紫色格子之间连min(w[x],w[y])的边...然后求最小割就好了...

注意题目输入是先输列,后输行;

注意两个紫色格子也要分开讨论,所以共有四层,可以发现每个2*4的区域都是一样的,所以可以预处理出颜色;

讨论时要注意不能把边建重了,特别是连源点,会点的边。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std; 

int getint()
{
	int i=0,f=1;char c;
	for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
	if(c=='-')f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

const int N=1e5+5,M=2e6+5,INF=0x3f3f3f3f;
const int color[2][4]={2,1,4,3,1,2,3,4};
const int fx[4]={0,1,0,-1};
const int fy[4]={-1,0,1,0};
int r,c,n,cnt,src,des;
int val[N],type[N];
int tot=1,first[N],cur[N],step[N],next[M],to[M],cap[M],from[M];
bool visit[N];
map<pair<int,int>,int>p;

inline void add(const int &x,const int &y,const int &w)
{
	next[++tot]=first[x],first[x]=tot,to[tot]=y,cap[tot]=w,from[tot]=x;
	next[++tot]=first[y],first[y]=tot,to[tot]=x,cap[tot]=0;
}

inline bool bfs()
{
	static int que[N];
	int tail=1;
	for(int i=src;i<=des;i++)step[i]=-1,cur[i]=first[i];
	que[tail]=src,step[src]=0;
	for(int head=1;head<=tail;head++)
	{
		int u=que[head];
		for(int e=first[u];e;e=next[e])
		{
			int v=to[e];
			if(cap[e]>0&&step[v]==-1)
			{
				step[v]=step[u]+1;
				que[++tail]=v;
				if(v==des)return true;
			}
		}
	}
	return false;
}

inline int dinic(const int &u,const int &flow)
{
	if(u==des)return flow;
	int res=0;
	for(int &e=cur[u];e;e=next[e])
	{
		int v=to[e];
		if(cap[e]>0&&step[v]>step[u])
		{
			int delta=dinic(v,min(flow-res,cap[e]));
			if(delta)
			{
				cap[e]-=delta,cap[e^1]+=delta;
				res+=delta;if(res==flow)break;
			}
		}
	}
	if(res!=flow)step[u]=-1;
	return res;
}

inline int maxflow()
{
	int ans=0;
	while(bfs())ans+=dinic(src,INF); 
	return ans;
}

int main()
{
	//freopen("lx.in","r",stdin);
	r=getint(),c=getint(),n=getint();
	src=0,des=n+1;
	int x,y,u,v;
	for(int i=1;i<=n;i++)
	{
		y=getint(),x=getint(),val[i]=getint();
		p[make_pair(x,y)]=i;
		type[i]=color[x%2][y%4];
	}
	map<pair<int,int>,int>::iterator it;
	for(it=p.begin();it!=p.end();it++)
	{
		x=it->first.first;
		y=it->first.second;
		u=it->second;
		if(type[u]==3)
			for(int k=0;k<4;k++)
			{
				int nowx=x+fx[k],nowy=y+fy[k];
				if(p.count(make_pair(nowx,nowy)))
				{
					v=p[make_pair(nowx,nowy)];
					if(type[v]==4)
					{
						if(!visit[v])add(src,v,val[v]),visit[v]=true;
						add(v,u,INF);
					}
					if(type[v]==2)
						add(u,v,min(val[u],val[v]));
				}
			}
		if(type[u]==2)
			for(int k=0;k<4;k++)
			{
				int nowx=x+fx[k],nowy=y+fy[k];
				if(p.count(make_pair(nowx,nowy)))
				{
					v=p[make_pair(nowx,nowy)];
					if(type[v]==1)
					{
						if(!visit[v])add(v,des,val[v]),visit[v]=true;
						add(u,v,INF);
					}
				}
			}
	}
	cout<<maxflow();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值