1. 程式人生 > >[bzoj1324]Exca王者之劍_最小割

[bzoj1324]Exca王者之劍_最小割

mes 小結 bits def ace fread min lin ref

Exca王者之劍 bzoj-1324

題目大意:題目鏈接。

註釋:略。


想法

最小割經典模型。

所有格子向源點連權值為格子權值的邊。

將棋盤黑白染色後白點反轉源匯。

如果兩個格子相鄰那麽黑點向白點連$inf$的有向邊。

求最小割即可。

開始把所有點的權值都加上,如果被割掉那麽就表示這個格子不選。

Code

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f 
#define N 100010 
using namespace std;
queue<int>q;
int to[N<<1],nxt[N<<1],tot=1,val[N<<1],head[N],dis[N],S,T;
int d1[]={-1,1,0,0};
int d2[]={0,0,1,-1};
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {int x=0,f=1; char c=nc(); while(c<48) {if(c==‘-‘) f=-1; c=nc();} while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x*f;}
inline void add(int x,int y,int z)
{
	to[++tot]=y; val[tot]=z; nxt[tot]=head[x]; head[x]=tot;
	to[++tot]=x; val[tot]=0; nxt[tot]=head[y]; head[y]=tot;
}
bool bfs()
{
	memset(dis,-1,sizeof dis); while(!q.empty()) q.pop();
	dis[S]=0; q.push(S); while(!q.empty())
	{
		int x=q.front(); q.pop(); for(int i=head[x];i;i=nxt[i]) if(dis[to[i]]==-1&&val[i]>0)
		{
			dis[to[i]]=dis[x]+1; q.push(to[i]);
			if(to[i]==T) return true;
		}
	}
	return false;
}
int dinic(int x,int fl)
{
	int tmp=fl; if(x==T) return fl; for(int i=head[x];i;i=nxt[i]) if(dis[to[i]]==dis[x]+1&&val[i]>0)
	{
		int mdl=dinic(to[i],min(val[i],tmp));
		if(!mdl) dis[to[i]]=-1;
		tmp-=mdl; val[i]-=mdl; val[i^1]+=mdl;
		if(!tmp) break;
	}
	return fl-tmp;
}
int main()
{
	int ans=0;
	int n=rd(),m=rd(); T=n*m+1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
	{
		int v=rd(); ans+=v;
		if((i+j)&1) add(S,(i-1)*m+j,v),add((i-1)*m+j,T,0);
		else add((i-1)*m+j,T,v),add(S,(i-1)*m+j,0);
		for(int k=0;k<4;k++)
		{
			int x=i+d1[k],y=j+d2[k];
			if(x>=1&&x<=n&&y>=1&&y<=m) ((i+j)&1)?
			(add((i-1)*m+j,(x-1)*m+y,inf)):
			(add((x-1)*m+y,(i-1)*m+j,inf));
		}
	}
	while(bfs()) ans-=dinic(S,1<<30);
	cout << ans << endl ;
	return 0;
}

小結:最小割的建模還是比較具有規律性的。

[bzoj1324]Exca王者之劍_最小割