【费用流】疯狂的方格取数 vijos 1653

P1653疯狂的方格取数  -  Vijos

背景

Due to the talent of talent123,当talent123做完NOIP考了两次的二取方格数和vijos中的三取方格数后,突发奇想....

描述

在一个宽M,长N的矩阵中,请你编一个程序,n次从矩阵的左上角走到矩阵的右下角,每到一处,就取走该处的数字,请你选择一
种走法使取得的数字的和最大,并输出其最大值。其中:3<=M<=20   M<=N<=100   1<=n<=10  
如输入数据:
3 10 13  
0 1 2 3 4 9 7 1 3 1
9 1 2 2 3 6 7 8 1 2
1 2 3 4 5 9 8 7 6 1
9 7 1 3 1 9 1 2 2 3
6 7 8 1 2 1 2 3 4 5
9 1 2 2 3 6 7 8 1 2
1 2 3 4 5 9 8 7 6 1
9 7 1 3 1 9 1 2 2 3
6 7 8 1 2 1 2 3 4 5
9 1 2 2 3 6 7 8 1 2
1 2 3 4 5 9 8 7 6 1
9 7 1 3 1 9 1 2 2 3
6 7 8 1 2 1 2 3 4 0
其中n=3
    M=10
    N=13
即当n=3时,就相当于是3取方格数。

对于以上的数据:
将输出:297
//注:如过你想到了无记忆性搜所的方法(不管你怎样优化),你可以直接放弃这道题了。

//提示1:动态规划如果用的是二位数组,规模为100*100000即可。

//提示2:如果你坚信自己的程序已经无可优化了,可有2个数据依然超时,那么告诉你,存在数据有M<n的情况!!!

格式

输入格式

第一行:三个整数:n M N
以下的N行每行M个数字,代表你要处理的矩阵。

 

输出格式

只有一行:你所取得的数字的和。

样例1

样例输入1

4 6 7
0 2 3 4 5 6
6 5 4 3 2 1
0 9 8 7 6 5
12 3 4 5 6 7
0 0 0 1 2 3
12 23 34 45 1 23
4 5 6 6 1 0

样例输出1

265






这一题是很经典的费用流题目,加入一个超级源到第一个点,费用为0,流量为K

同样加入超级汇


把每个点拆成两个点(如点和出点),入点和出点之间连一条流量为1和费用为权值的边,再连一条流量为inf,费用为0的点,这就表示每个点只能取一次,但是可以重复走了

然后再把整个图连起来


跑一次费用流,ok~


第一次写邻接表式的费用流,调了很久


测评情况(Vijos)




/*http://blog.csdn.net/jiangzh7
By Jiangzh*/
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=100+10;
const int P=N*N*5;
const int inf=0x3f3f3f3f;

int n,m,K;
int map[N][N];
struct EG{int from,to,flow,cost,next;}edge[P];
int head[P],L;
int S,T;
bool inQ[P];
int dist[P];
int pre[P];
queue<int> Q;

void read()
{
	scanf("%d%d%d",&K,&m,&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&map[i][j]);
}

inline int In (int x,int y) { return ((x-1)*m+y)*2-1; }
inline int Out(int x,int y) { return ((x-1)*m+y)*2; }

void add_edge(int x,int y,int z,int w)
{
	edge[L]=(EG){x,y,z,w ,head[x]};
	head[x]=L++;
	edge[L]=(EG){y,x,0,-w,head[y]};
	head[y]=L++;
}

void make_map()
{
	memset(head,-1,sizeof(head));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			add_edge(In(i,j),Out(i,j),1,map[i][j]);
			add_edge(In(i,j),Out(i,j),inf,0);
			if(j<m) add_edge(Out(i,j),In(i,j+1),inf,0);
			if(i<n) add_edge(Out(i,j),In(i+1,j),inf,0);
		}
	S=0;T=Out(n,m)+1;
	add_edge(S,In(1,1),K,0);
	add_edge(Out(n,m),T,inf,0);
}

bool spfa()
{
	for(int i=S;i<=T;i++) dist[i]=-inf;
	memset(inQ,0,sizeof(inQ));
	dist[S]=0;Q.push(S);
	while(!Q.empty())
	{
		int x=Q.front();
		Q.pop();inQ[x]=false;
		for(int i=head[x];i!=-1;i=edge[i].next)
		{
			if(edge[i].flow>0 && dist[edge[i].to]<dist[x]+edge[i].cost)
			{
				pre[edge[i].to]=i;
				dist[edge[i].to]=dist[x]+edge[i].cost;
				if(!inQ[edge[i].to])
				{
					inQ[edge[i].to]=true;
					Q.push(edge[i].to);
				}
			}
		}
	}
	return dist[T]!=-inf;
}

void work()
{
	make_map();
	memset(pre,-1,sizeof(pre));
	int maxcost=0,maxflow=0;
	while(spfa())
	{
		int res=inf;
		for(int i=T;i!=S;i=edge[pre[i]^1].to)
		{
			res=min(res,edge[pre[i]].flow);
		}
		for(int i=T;i!=S;i=edge[pre[i]^1].to)
		{
			edge[pre[i]].flow-=res;
			edge[pre[i]^1].flow+=res;
		}
		maxflow+=res;
		maxcost+=res*dist[T];
	}
	printf("%d\n",maxcost);
}

int main()
{
	freopen("vijos1653.in","r",stdin);
	freopen("vijos1653.out","w",stdout);
	read();
	work();
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值