【消圈】poj2175

9 篇文章 0 订阅

poj2175只用到了一次找负圈,因此比较快,如果一直找下去,效率应该很低

首先是用拓展km直接做——375ms

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
const int oo=1073741819;
using namespace std;
int x[500],y[500],p[500],q[500];
int a[500],b[500],lx[500],ly[500],vx[500],vy[500];
int f[500],g[500],slack[500],w[500][500],c[500][500];
int n,m;
bool km(int x)
{
	if (vx[x]) return 0;
	vx[x]=1;
	for (int i=1;i<=m;i++) {
		if (vy[i]) continue;
		int tmp=lx[x]+ly[i]-w[x][i];
		if (!tmp) {
			vy[i]=1;
			if (b[i]) {
				f[x]=i,g[x]=0;
				return 1;
			}
			for (int j=1;j<=n;j++)
				if (c[j][i] && km(j)) {
					f[x]=i,g[x]=j;
					return 1;
				}
		}
		else slack[i]=min(slack[i],tmp);
	}
	return 0;
}
int push(int x)
{
	int d=a[x];
	for (int i=x;i;i=g[i]) {
		if (g[i]) d=min(d,c[g[i]][f[i]]);
		else d=min(d,b[f[i]]);
	}
	int sum=0;
	a[x]-=d;
	for (int i=x;i;i=g[i]) {
		if (g[i]) sum-=d*w[g[i]][f[i]],c[g[i]][f[i]]-=d;
		else b[f[i]]-=d;
		sum+=d*w[i][f[i]],c[i][f[i]]+=d;
	}
	return sum;
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	for (;scanf("%d%d",&n,&m)==2;) {
		for (int i=1;i<=n;i++) {
			scanf("%d%d%d",&x[i],&y[i],&a[i]);
			lx[i]=0,ly[i]=0;
		}
		for (int i=1;i<=m;i++)
			scanf("%d%d%d",&p[i],&q[i],&b[i]);
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++) {
				w[i][j]=-(abs(x[i]-p[j])+abs(y[i]-q[j])+1);
				c[i][j]=0;
			}
		int tot=0;
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++) {
				int x;
				scanf("%d",&x);
				tot+=w[i][j]*x;
			}
		int ans=0;
		for (int i=1;i<=n;i++) {
			for (;a[i];) {
				for (int j=1;j<=m;j++) slack[j]=oo;
				for (;a[i];) {
					for (int j=1;j<=n;j++) vx[j]=0;
					for (int j=1;j<=m;j++) vy[j]=0;
					if (km(i))
						ans+=push(i);
					else break;
				}
				if (!a[i]) break;
				int d=oo;
				for (int j=1;j<=m;j++) 
					if (!vy[j]) d=min(d,slack[j]);
				for (int j=1;j<=n;j++)
					if (vx[j]) lx[j]-=d;
				for (int j=1;j<=m;j++)
					if (vy[j]) ly[j]+=d;
			}
		}
		if (ans==tot) printf("OPTIMAL\n");
		else {
			printf("SUBOPTIMAL\n");
			for (int i=1;i<=n;i++) {
				for (int j=1;j<m;j++) printf("%d ",c[i][j]);
				printf("%d\n",c[i][m]);
			}
		}
	}
	return 0;
} 

然后是原始对偶费用流直接做,tle,本机1.3s+,如果常数再小点不知能不能过

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
const int oo=1073741819;
using namespace std;
int b[500][500],flow[500][500],c[500][500];
int B[4096],n,m,s,t,ans,phi,w_time,m1;
int x[500],y[500],p[500],q[500],d[500];
int w[500][500],flag[500];
void change(int x,int w)
{
	d[x]=w;
	for (x=((x+m1)>>1);x;x>>=1)
		if (d[B[x<<1]]>d[B[(x<<1)^1]]) B[x]=B[(x<<1)^1];
		else B[x]=B[x<<1];
}
bool spfa(int s,int t)
{
	int ne,na;
	for (int i=0;i<=t;i++) d[i]=p[i]=oo;
	change(t,0),p[t]=0;
	for (;d[B[1]]!=oo;) {
		ne=B[1];
		change(ne,oo);
		for (int i=1;i<=b[ne][0];i++) {
			na=b[ne][i];
			if (flow[na][ne] && p[ne]+c[na][ne]<p[na]) {
				p[na]=p[ne]+c[na][ne];
				change(na,p[na]);
			}
		}
	}
	if (p[s]>=oo) return 0;
	phi+=p[s];
	for (int i=1;i<=t;i++) 
		for (int j=1;j<=b[i][0];j++) {
			ne=b[i][j];
			c[i][ne]-=p[i]-p[ne];
		}
	return 1;
}
int dfs(int x,int low)
{
	if (t==x) {
		ans+=phi*low;
		return low;
	}
	int sum=0,tmp,ne;
	flag[x]=w_time;
	for (int i=1;i<=b[x][0];i++) {
		ne=b[x][i];
		if (flow[x][ne] && flag[ne]!=w_time && !c[x][ne]) {
			if (flow[x][ne]<low) tmp=dfs(ne,flow[x][ne]);
			else tmp=dfs(ne,low);
			flow[x][ne]-=tmp,flow[ne][x]+=tmp,sum+=tmp,low-=tmp;
			if (!low) break;
		}
	}
	return sum;
}
void origin()
{
	s=n+m+1,t=s+1;
	for (int i=1;i<=t;i++) b[i][0]=0;
	for (m1=1;m1<=t+2;m1<<=1) ;
	for (int i=1;i<=t;i++) B[i+m1]=i;
}
void link(int x,int y,int z,int cc)
{
	b[x][++b[x][0]]=y,flow[x][y]=z,c[x][y]=cc;
	b[y][++b[y][0]]=x,flow[y][x]=0,c[y][x]=-cc;
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	for (;scanf("%d%d",&n,&m)==2;) {
		origin();
		for (int i=1;i<=n;i++) {
			int z;
			scanf("%d%d%d",&x[i],&y[i],&z);
			link(s,i,z,0);
		}
		for (int i=1;i<=m;i++) {
			int z;
			scanf("%d%d%d",&p[i],&q[i],&z);
			link(i+n,t,z,0);
		}
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++) {
				w[i][j]=abs(x[i]-p[j])+abs(y[i]-q[j])+1;
				link(i,j+n,oo,w[i][j]);
			}
		int tot=0;
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++) {
				int x;
				scanf("%d",&x);
				tot+=w[i][j]*x;
			}
		ans=0;
		for (phi=0;spfa(s,t);) 
			for (;w_time++,dfs(s,oo);) ;
		if (ans==tot) printf("OPTIMAL\n");
		else {
			printf("SUBOPTIMAL\n");
			for (int i=1;i<=n;i++) {
				for (int j=1;j<m;j++) printf("%d ",flow[j+n][i]);
				printf("%d\n",flow[m+n][i]);
			}
		}
	}
	return 0;
} 

最后是找一次负圈,也并不比拓展km快

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
const int oo=1073741819;
using namespace std;
int b[500][500],flow[500][500],c[500][500];
int n,m,s,t,ans;
int x[500],y[500],p[500],q[500],d[500],cnt[500],pre[500];
int w[500][500];
int st[2000000],v[500];
void origin()
{
	s=n+m+1,t=s+1;
	for (int i=1;i<=t;i++) b[i][0]=0;
}
void link(int x,int y,int z,int cc)
{
	b[x][++b[x][0]]=y,flow[x][y]=z,c[x][y]=cc;
	b[y][++b[y][0]]=x,flow[y][x]=0,c[y][x]=-cc;
}
bool spfa(int s,int t)
{
	int h,r,ne,na;
	h=r=0;
	for (int i=1;i<=t;i++) {
		d[i]=0;
		st[++r]=i,v[i]=1,pre[i]=0,cnt[i]=1;
	}
	for (;h<r;) {
		ne=st[++h];
		for (int i=1;i<=b[ne][0];i++) {
			na=b[ne][i];
			if (flow[ne][na] && d[ne]+c[ne][na]<d[na]) {
				d[na]=d[ne]+c[ne][na],pre[na]=ne;
				if (!v[na]) {
					v[na]=1,st[++r]=na;
					cnt[na]++;
					if (cnt[na]>t) return 1;
				}
			}
		}
		v[ne]=0;
	}
	return 0;
}
int widen()
{
	int k;
	for (int i=1;i<=t;i++) {
		if (cnt[i]>t) k=i;
		v[i]=0;
	}
	int r=0;
	int sum=0;
	for (;!v[k];k=pre[k]) v[k]=1;
	st[r=1]=k;
	for (int i=pre[k];i!=k;i=pre[i]) st[++r]=i;
	for (int i=1;i<=r;i++) {
		v[st[i]]=0;
		flow[pre[st[i]]][st[i]]--;
		flow[st[i]][pre[st[i]]]++;
		sum+=c[pre[st[i]]][st[i]];
	}
	return sum;
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	for (;scanf("%d%d",&n,&m)==2;) {
		origin();
		for (int i=1;i<=n;i++) {
			int z;
			scanf("%d%d%d",&x[i],&y[i],&z);
			link(s,i,z,0);
		}
		for (int i=1;i<=m;i++) {
			int z;
			scanf("%d%d%d",&p[i],&q[i],&z);
			link(i+n,t,z,0);
		}
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++) {
				w[i][j]=abs(x[i]-p[j])+abs(y[i]-q[j])+1;
				link(i,j+n,oo,w[i][j]);
			}
		int tot=0;
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++) {
				int x;
				scanf("%d",&x);
				tot+=w[i][j]*x;
				flow[s][i]-=x,flow[i][j+n]-=x,flow[j+n][t]-=x;
				flow[i][s]+=x,flow[j+n][i]+=x,flow[t][j+n]+=x;
			}
		ans=tot;
		if (spfa(s,t)) ans+=widen();
		if (ans==tot) printf("OPTIMAL\n");
		else {
			printf("SUBOPTIMAL\n");
			for (int i=1;i<=n;i++) {
				for (int j=1;j<m;j++) printf("%d ",flow[j+n][i]);
				printf("%d\n",flow[m+n][i]);
			}
		}
	}
	return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值