题目描述
给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大
输入输出格式
输入格式:
第一行两个数n,k(1<=n<=50, 0<=k<=10)
接下来n行,每行n个数,分别表示矩阵的每个格子的数
输出格式:
一个数,为最大和
输入输出样例
输入样例#1:
3 1 1 2 3 0 2 1 1 4 2
输出样例#1:
11
说明
每个格子中的数不超过100
【题解】
先把每个点拆成2个A,B,
Ai->Bi之间连2条边,一条流量为1,费用为该格点上的权值,以计算费用,
另一条流量为INF,费用为0,保证通路,再把每个点扫一遍,
可以到达的点之间Bi->Aj连流量INF,费用0的边。
虚拟源st=0,st->A1(第一个点)连费用0,流量k的边,限制流量。
虚拟汇ed=n*n*2+1,,右下角的格点Bi->ed连费用0,流量k的边。。。
跑一遍最大费用最大流就行了。。
#include<cstdio> #include<iostream> using namespace std; const int N=110; const int M=N*N; const int inf=0x3f3f3f3f; struct node{ int v,next,cap,cost; }e[M*50];int tot=1; int n,m,k,S,T,ans,head[M],dis[M],pree[M],q[M*50]; bool vis[M]; void add(int x,int y,int cap,int cost){ e[++tot].v=y;e[tot].cap=cap;e[tot].cost=cost;e[tot].next=head[x];head[x]=tot; e[++tot].v=x;e[tot].cap=0;e[tot].cost=-cost;e[tot].next=head[y];head[y]=tot; } bool spfa(){ for(int i=S;i<=T;i++) vis[i]=0,dis[i]=-inf; int h=0,t=1; q[t]=S;dis[S]=0;vis[S]=1; while(h!=t){ int x=q[++h];vis[x]=0; for(int i=head[x];i;i=e[i].next){ int v=e[i].v; if(e[i].cap>0&&dis[v]<dis[x]+e[i].cost){ dis[v]=dis[x]+e[i].cost; pree[v]=i; if(!vis[v]){ vis[v]=1; q[++t]=v; } } } } return dis[T]>0; } void augment(){ int tmp=inf; for(int i=T;i!=S;i=e[pree[i]^1].v){ tmp=min(tmp,e[pree[i]].cap); } for(int i=T;i!=S;i=e[pree[i]^1].v){ ans+=tmp*e[pree[i]].cost; e[pree[i]].cap-=tmp; e[pree[i]^1].cap+=tmp; } } int main(){ scanf("%d%d",&n,&k);S=0;T=n*n*2+1; for(int i=1,now=1,plus=n*n,x;i<=n;i++){ for(int j=1;j<=n;j++,now++){ scanf("%d",&x); add(now,now+plus,1,x); add(now,now+plus,inf,0); if(j<n) add(now+plus,now+1,inf,0); if(i<n) add(now+plus,now+n,inf,0); } } add(S,S+1,k,0); add(T-1,T,k,0); while(spfa()) augment(); printf("%d",ans); return 0; }