[HDOJ 4862] Jump [费用流]

69 篇文章 0 订阅
4 篇文章 0 订阅

给定一个地图,范围10*10,每个位置上有一个数字,你可以执行小于等于k次操作,每次操作为在地图中选取一个点,然后可以从这个点往右或者下跳若干次,每次跳跃的花费为两个点之间的距离减一,若两个点上的数字相同,则可以得到与这个数字相同的分数。问经过小于等于k次操作之后,访问每个点有且只有一次的前提下,能够得到的最高分数是多少?

把每个点拆成入点和出点,每个点的入点到出点有一条流量为1,费用为-MAX的边,这样保证每个点都走且只走一次。每个点的出点到其他点的入点连边,流量为1,费用为这次跳跃所能得到的分数的相反数。每个点的出点向汇连边,源向每个点的入点连边,流量为1,费用为0。超级源向源流流量为k,费用为0的边。

这样,从超级源向汇的最小费用最大流的大小的相反数,就是恰好执行k次操作时,所能的到的最大分数。

枚举执行的操作数,每次在残量网络上将超级源向源的边的可流流量增1,这样每次求最大流其实只是一次SPFA。

若k<min(n,m),则无法流通。

#include <cstdio>
#include <cstring>

const int MAXINT=(1<<30);

struct MinCostFlow {
	static const int MAXNODE=1000;
	static const int MAXEDGE=3000;
	struct Node {
		int fe,c,f,fromNode,curCost,fromEdge;
		bool inque;
	};
	struct Edge {
		int f,t,ne,c;
	};
	Node a[MAXNODE];
	Edge b[MAXEDGE*2];
	int d[MAXNODE];
	int s,t,n,p,cost;
	void clear(int nn,int ss,int tt) {
		n=nn;s=ss;t=tt;
		for (int i=0;i<=n;i++) {
			a[i].fe=-1;
			a[i].f=0;
			a[i].c=0;
		}
		p=0;
		cost=0;
	}
	int putedge(int x,int y,int f,int c) {
		//printf("%d %d %d %d\n",x,y,f,c);
		b[p].ne=a[x].fe;
		b[p].t=y;
		b[p].f=f;
		b[p].c=c;
		a[x].fe=p;
		p++;
		b[p].ne=a[y].fe;
		b[p].t=x;
		b[p].f=0;
		b[p].c=-c;
		a[y].fe=p;
		p++;
		return p-2;
	}
	bool spfa() {
		int i,p,q,j;
		for (i=0;i<=n;i++) {
			a[i].curCost=MAXINT;
			a[i].fromNode=-1;
			a[i].fromEdge=-1;
			a[i].inque=false;
		}
		p=q=0;
		d[q++]=s;
		a[s].inque=true;
		a[s].curCost=0;
		while (p!=q) {
			i=d[p];
			for (j=a[i].fe;j!=-1;j=b[j].ne) {
				if (b[j].f>0&&b[j].c+a[i].curCost<a[b[j].t].curCost) {
					a[b[j].t].curCost=a[i].curCost+b[j].c;
					a[b[j].t].fromNode=i;
					a[b[j].t].fromEdge=j;
					if (a[b[j].t].inque==false) {
						a[b[j].t].inque=true;
						d[q]=b[j].t;
						q=(q+1)%MAXNODE;
					}
				}
			}
			a[i].inque=false;
			p=(p+1)%MAXNODE;
		}
		if (a[t].curCost==MAXINT) return false;
		p=MAXINT;
		q=0;
		for (i=t;i!=s;i=a[i].fromNode) {
			d[q++]=i;
			if (p>b[a[i].fromEdge].f) p=b[a[i].fromEdge].f;
		}
		j=0;
		for (i=q-1;i>=0;i--) {
			j+=b[a[d[i]].fromEdge].c*p;
			b[a[d[i]].fromEdge].f-=p;
			b[a[d[i]].fromEdge^1].f+=p;
			a[d[i]].c+=j;
			a[d[i]].f+=p;
		}
		return true;
	}
	void flow() {
		int i;
		a[s].f=MAXINT;
		while (spfa()) ;
	}
	int getFlow() {
		return a[t].f;
	}
	int getCost() {
		return a[t].c;
	}
};

MinCostFlow c;
int n,m,kk,nm;
int a[10][10];
int ans;

int tran(int i,int j) {
	return i*m+j+1;
}

int main() {
	int i,j,k,t,tt,curk;
	scanf("%d",&tt);
	for (t=1;t<=tt;t++) {
		scanf("%d%d%d",&n,&m,&kk);
		for (i=0;i<n;i++) {
			for (j=0;j<m;j++) {
				char c;
				scanf(" %c",&c);
				a[i][j]=c-'0';
			}
		}
		nm=n*m;
		ans=-1;
		c.clear(nm+nm+3,nm+nm+2,nm+nm+3);
		for (i=0;i<n;i++) {
			for (j=0;j<m;j++) {
				c.putedge(nm+nm+1,tran(i,j),1,0);
				c.putedge(tran(i,j)+nm,nm+nm+3,1,0);
				c.putedge(tran(i,j),tran(i,j)+nm,1,-20000);
			}
		}
		for (i=0;i<n;i++) {
			for (j=0;j<m;j++) {
				for (k=i+1;k<n;k++) {
					int tmp=-k+i+1;
					if (a[i][j]==a[k][j]) tmp+=a[i][j];
					c.putedge(tran(i,j)+nm,tran(k,j),1,-tmp);
				}
				for (k=j+1;k<m;k++) {
					int tmp=-k+j+1;
					if (a[i][j]==a[i][k]) tmp+=a[i][j];
					c.putedge(tran(i,j)+nm,tran(i,k),1,-tmp);
				}
			}
		}
		curk=(n>m?m:n);
		int edg=c.putedge(nm+nm+2,nm+nm+1,curk,0);
		for (;curk<=kk;curk++) {
			c.flow();
			if (c.getFlow()==curk) {
				int tmp=-(c.getCost()+20000*nm);
				if (ans==-1||tmp>ans) ans=tmp;
			}
			c.b[edg].f++;
		}
		printf("Case %d : %d\n",t,ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值