首页 > 代码库 > 【BZOJ2229】【ZJOI2011】最小割 {没有错,这道题的算法跟题帽是一样的!!!}

【BZOJ2229】【ZJOI2011】最小割 {没有错,这道题的算法跟题帽是一样的!!!}

题解:分治求最小割。


【l……r】里任意找两个作为s、t(不妨把s设为l位置上的点,t设为r位置上的点)求最小割,两层for循环枚举修改map[i][j]即两点间最小割值。

然后一部分属于S集,一部分属于T集,分治【l,L】,【R,r】,每次求完最小割值都全局进行修改。


最后每次询问暴力做就好了,无需任何优化即可AC。


代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200
#define M 7000
#define inf 0x3f3f3f3f
#define ci crs[i]
#define cj crs[j]
using namespace std;
struct KSD
{
	int v,len,next,flow;
}e[M];
int head[N],cnt;
inline void add(int u,int v,int len)
{
	cnt++;
	e[cnt].v=v;
	e[cnt].flow=e[cnt].len=len;
	e[cnt].next=head[u];
	head[u]=cnt;
}
inline void resume(){for(int i=2;i<=cnt;i++)e[i].len=e[i].flow;}

int crs[N],tcrs[N];
bool flag[N];
int map[N][N];

int d[N],s,t;
int n,m;
queue<int>q;

bool bfs()
{
	int i,u,v;
	memset(d,0,sizeof(d));
	while(!q.empty())q.pop();
	q.push(s),d[s]=1;
	while(!q.empty())
	{
		u=q.front(),q.pop();
		for(i=head[u];i;i=e[i].next)
		{
			v=e[i].v;
			if(e[i].len&&!d[v])
			{
				d[v]=d[u]+1;
				if(v==t)return 1;
				q.push(v);
			}
		}
	}
	return 0;
}
int dinic(int x,int flow)
{
	if(x==t)return flow;
	int remain=flow,i,v,k;
	for(i=head[x];i&&remain;i=e[i].next)
	{
		if(d[v=e[i].v]==d[x]+1&&e[i].len)
		{
			k=dinic(v,min(remain,e[i].len));
			if(!k)d[v]=0;
			e[i].len-=k,e[i^1].len+=k;
			remain-=k;
		}
	}
	return flow-remain;
}
void dfs(int x)
{
	int i,v;
	flag[x]=1;
	for(i=head[x];i;i=e[i].next)if(!flag[v=e[i].v]&&e[i].len)dfs(v);
}
void dar(int l,int r) // divide and rule 分治
{
	int i,j,maxflow=0;
	s=crs[l],t=crs[r];
	resume();
	while(bfs())maxflow+=dinic(s,inf);
	memset(flag,0,sizeof(flag)),dfs(s);

	for(i=1;i<=n;i++)for(j=1;j<=n;j++)
		if(flag[ci]^flag[cj])
			map[ci][cj]=min(map[ci][cj],maxflow);

	int L=l-1,R=r+1;
	for(i=l;i<=r;i++)(flag[ci]?tcrs[++L]:tcrs[--R])=ci;
	for(i=l;i<=r;i++)crs[i]=tcrs[i];

	if(l<L)dar(l,L);
	if(R<r)dar(R,r);
}
int main()
{
	int i,j,k,g;
	int a,b,c;
	for(scanf("%d",&g);g--;)
	{
		cnt=1;
		memset(head,0,sizeof(head));
		memset(map,0x3f,sizeof(map));
		for(scanf("%d%d",&n,&m),cnt=1;m--;)
		{
			scanf("%d%d%d",&a,&b,&c);
			add(a,b,c),add(b,a,c);
		}
		for(i=1;i<=n;i++)crs[i]=i;
		dar(1,n);
		for(scanf("%d",&m);m--;)
		{
			scanf("%d",&k);
			int ans=0;
			for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(map[i][j]<=k)ans++;
			printf("%d\n",ans>>1);
		}
		puts("");
	}
	return 0;
}


话说我再贴个错误思想的代码吧

:SW算法求全局最小割,然后两边任意点对间的最小割值肯定都是这个值,然后两边分治。

SW算法相关博客(点此即链接):话说我学SW就是想用来解决这个的233……

这个很有正确性的样子嘛,为什么会挂呢?

就姑且留给读者思考吧,这个是确实有问(fan)题(li)的(跟SW算法实现过程以及分治后的一些值有关系)


代码(WA):

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 160
#define inf 0x3f3f3f3f
using namespace std;
int n,m,q,crs[N],tcrs[N],cnt;
struct ANS
{
	int x,sum;
	ANS(int _x=0,int _sum=0):x(_x),sum(_sum){}
	bool operator < (const ANS &a)const{return x<a.x;}
}ans[N];
int map[N][N];
 
int dis[N],f[N];
bool vis[N],flag[N];
int next[N],final[N];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void Stoer_Wagner(int l,int r)
{
	int ret=inf;
	int i,j,_n,rnd;
	int u,v,temp,lastu=0;
	for(i=l;i<=r;i++)f[i]=next[i]=final[i]=i,flag[i]=0;
	for(_n=r-l+1;_n>1;_n--)
	{
		for(i=l;i<=r;i++)dis[i]=vis[i]=0;
		for(rnd=1;rnd<=_n;rnd++)
		{
			lastu=u;
			temp=-1;
			for(i=l;i<=r;i++)if(f[i]==i&&!vis[i])
			{
				if(temp<dis[i])temp=dis[i],u=i;
			}
			vis[u]=1;
			bool flag=0;
			for(j=u;;j=next[j])
			{
				if(j==final[u])flag=1;
				for(i=l;i<=r;i++)if(map[crs[u]][crs[i]])
				{
					v=find(i);
					if(v!=u)dis[v]+=map[crs[u]][crs[i]];
				}
				if(flag)break;
			}
		}
		if(ret>=dis[u])  
		{
			for(i=l;i<=r;i++)flag[i]=(f[i]!=i||i==u);
			ret=dis[u];
		}
		f[u]=lastu;
		next[final[lastu]]=u;
		final[lastu]=final[u];
	}
	int L=l-1,R=r+1;
	for(i=l;i<=r;i++)
	{
		if(flag[i])tcrs[++L]=crs[i];
		else tcrs[--R]=crs[i];
	}
	for(i=l;i<=r;i++)crs[i]=tcrs[i];
	ans[++cnt]=ANS(ret,(R-l)*(r-L));
	if(R-l>1)Stoer_Wagner(l,L);
	if(r-L>1)Stoer_Wagner(R,r);
	return ;
}
int sum[N];
int main()
{
//  freopen("test.in","r",stdin);
	int i,k,g;
	int a,b,c;
	for(scanf("%d",&g);g--;)
	{
		memset(map,0,sizeof(map));
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)crs[i]=i;
		for(i=1;i<=m;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			if(a!=b)map[a][b]+=c,map[b][a]+=c;
		}
		Stoer_Wagner(1,n);
		sort(ans+1,ans+cnt+1);
		for(i=1;i<=cnt;i++)sum[i]=sum[i-1]+ans[i].sum;
 
		scanf("%d",&q);
		while(q--)
		{
			scanf("%d",&k);
			int l=1,r=cnt,mid,ret;
			while(l<r)
			{
				if(r-l<=3)
				{
					ret=0;
					for(i=l;i<=r;i++)if(ans[i].x<=k)ret=i;
					break;
				}
				mid=l+r>>1;
				if(ans[i].x<=k)l=mid;
				else r=mid-1;
			}
			printf("%d\n",sum[ret]);
		}
	}
	return 0;
}



【BZOJ2229】【ZJOI2011】最小割 {没有错,这道题的算法跟题帽是一样的!!!}