[洛谷P2045]方格取數加強版
阿新 • • 發佈:2017-08-20
開始 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]方格取數加強版