Description:
给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大
Input:
第一行两个数n,k(1<=n<=50, 0<=k<=10)
接下来n行,每行n个数,分别表示矩阵的每个格子的数
Output:
一个数,为最大和
思路:仍旧是拆点 因为每个点都有一个限制K和一个价值V 所以将一个点拆成两个点,对相邻的点连两条边,一条费用为V,限制为1, 一条费用为0,限制为K - 1。然后跑个最大费用最大流即可
#include<iostream> #include<cstring> #include<queue> using namespace std; const int N = 50100, M = 2000100; int d[N], incf[N], pre[N], n, k, s, t, maxflow, ans; bool vis[N]; int head[N],now; struct edges{ int to,next,lim,w; }edge[N<<1]; void add(int x,int y,int z,int c){ edge[++now] = {y,head[x],z,c}; head[x] = now; edge[++now] = {x,head[y],0,-c}; head[y] = now; } int id(int i,int j,int k){ return (i - 1)*n + j + k * n * n;} bool spfa(){ queue<int> q; memset(d,0xcf,sizeof(d)); memset(vis,0,sizeof(vis)); q.push(s); d[s] = 0; vis[s] = 1; incf[s] = 1e9; while(!q.empty()){ int x = q.front(); vis[x] = 0; q.pop(); for(int i = head[x]; i; i = edge[i].next){ int v = edge[i].to; if(!edge[i].lim) continue; if(d[v] < d[x] + edge[i].w){ d[v] = d[x] + edge[i].w; incf[v] = min(incf[x], edge[i].lim); pre[v] = i; if(!vis[v]) vis[v] = 1, q.push(v); } } } if(d[t] == 0xcfcfcfcf) return 0; return 1; } void update(){ int x = t; while(x != s){ int i = pre[x]; edge[i].lim -= incf[t]; edge[i ^ 1].lim += incf[t]; x = edge[i ^ 1].to; } maxflow += incf[t]; ans += d[t] * incf[t]; } int main(){ ios::sync_with_stdio(false); cin>>n>>k; s = 1, t = 2*n*n; now = 1; int x; for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++){ cin>>x; add(id(i,j,0),id(i,j,1),1,x); add(id(i,j,0),id(i,j,1),k - 1,0); if(j < n) add(id(i,j,1),id(i,j+1,0),k,0); if(i < n) add(id(i,j,1),id(i+1,j,0),k,0); } while(spfa()) update(); cout<<ans<<endl; return 0; }