1. 程式人生 > >[洛谷P2045]方格取數加強版

[洛谷P2045]方格取數加強版

開始 strong als brush for class head 最大流 連接

題目大意:有一個n*n的矩陣,每個格子有一個非負整數,規定一個人從(1,1)開始,只能往右或下走,走到(n,n)為止,並把沿途的數取走,取走後數變為0。這個人共取n次,求取得的數的最大總和。

解題思路:由於取多少次不確定,所以不能用dp。

我們發現,一個格子只能從左邊或上面走來,且數只能取到一次,那麽我們可以把此題轉化為最大費用最大流問題。首先拆點,將一個點拆成x和y,然後從x到y連一條容量為1,流量為x(x為這格的數)的邊,然後再連一條容量為inf,費用為0的邊,這樣即可保證一個點可以走多次,而數只能取一次。然後連接a和b時,從a的y向b的x連一條容量為inf,費用為0的邊。最後跑最大費用最大流即可。

實現時對於(i,j),我們把這個點的x編號為$(i-1)*n+j$,y編號為$[(i-1)*n+j]*n^2$即可。

以下為EK算法代碼。

C++ Code:

#include<cstdio>
#include<vector>
#include<queue>
#include<string.h>
using namespace std;
#define inf 0x3f3f3f3f
#define N 1000000
struct edge{
	int from,to,cap,cost,nxt;
}e[N];
int n,k,dis[N],a[N],pree[N],head[N],cnt;
bool vis[N];
queue<int>q;
inline void addedge(int from,int to,int cap,int cost){
	e[++cnt]=(edge){from,to,cap,cost,head[from]};
	head[from]=cnt;
	e[++cnt]=(edge){to,from,0,-cost,head[to]};
	head[to]=cnt;
}
bool spfa(int s,int t,int& flow,int& cost){
	memset(pree,0,sizeof(pree));
	memset(a,0x3f,sizeof(a));
	memset(dis,200,sizeof(dis));
	memset(vis,0,sizeof(vis));
	vis[s]=1;
	dis[s]=0;
	q.push(s);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=e[i].nxt){
			if(e[i].cap>0&&dis[e[i].to]<dis[u]+e[i].cost){
				dis[e[i].to]=dis[u]+e[i].cost;
				pree[e[i].to]=i;
				if(a[u]>e[i].cap)a[e[i].to]=e[i].cap;else a[e[i].to]=a[u];
				if(!vis[e[i].to]){
					vis[e[i].to]=1;
					q.push(e[i].to);
				}
			}
		}
	}
	if(dis[t]<1)return false;
	flow+=a[t];
	cost+=a[t]*dis[t];
	for(int i=t;i!=s;i=e[pree[i]].from){
		e[pree[i]].cap-=a[t];
		e[pree[i]^1].cap+=a[t];
	}
	return true;
}
int main(){
	cnt=1;
	scanf("%d%d",&n,&k);
	int m=n*n;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			int x;
			scanf("%d",&x);
			addedge((i-1)*n+j,(i-1)*n+j+m,inf,0);
			addedge((i-1)*n+j,(i-1)*n+j+m,1,x);
			if(i>1){
				addedge((i-2)*n+j+m,(i-1)*n+j,inf,0);
			}
			if(j>1){
				addedge((i-1)*n+j-1+m,(i-1)*n+j,inf,0);
			}
			if(i==1&&j==1){
				addedge(0,1,inf,0);
			}
		}
	}
	int flow=0,cost=0;
	while(k--)
	if(!spfa(0,m<<1,flow,cost))break;
	printf("%d\n",cost);
	return 0;
}

[洛谷P2045]方格取數加強版