源点和左上角建边,费用0流量k,右下角和汇点建边,费用0,流量k。
将每个点拆成2个点,a,A。
a->A建一条费用为当前点权值的相反数,流量为1的点,代表选这个点且只能选1次。
a->A再建一条费用为0,流量为INF的点,代表不选这个点。
A->右,A->下建一条费用为0,流量为INF的边,过渡用。
求最小费用流,相反数就是最大费用流。
#include<stdio.h> #include<queue> #include<algorithm> #include<cstring> using namespace std; #define MAXN 5555 #define MAXM 1000000 #define INF 0x3f3f3f3f struct node { int u,v,f,c,next; }e[MAXM]; int n,k,head[MAXN],pre[MAXN],dist[MAXN],vis[MAXN]; int map[55][55]; int en,s,t,maxflow; //s源点,t汇点 void add(int u,int v,int c,int f)//加边 { e[en].u=u; e[en].v=v; e[en].c=c; e[en].f=f; e[en].next=head[u]; head[u]=en++; e[en].u=v; e[en].v=u; e[en].c=-c; e[en].f=0; e[en].next=head[v]; head[v]=en++; } int q[1000000]; int spfa() { int i,u,v; memset(pre,-1,sizeof(int)*(t+1)); memset(vis,0,sizeof(int)*(t+1)); memset(dist,0x3f,sizeof(int)*(t+1)); dist[s]=0; vis[s]=1; int front=0,rear=0; q[rear++]=s; while(front<rear) { u=q[front++]; for(i=head[u];i!=-1;i=e[i].next) { v=e[i].v; if(e[i].f>0&&dist[u]+e[i].c<dist[v]) { dist[v]=dist[u]+e[i].c; pre[v]=i; if(!vis[v]) { vis[v]=1; q[rear++]=v; } } } vis[u]=0; } if(dist[t]==INF) return 0; return 1; } void add() { int v; int minf=INF; for(v=pre[t];e[v].u!=s;v=pre[e[v].u]) minf=min(minf,e[v].f); for(v=pre[t];e[v].u!=s;v=pre[e[v].u]) { e[v].f-=minf; e[v^1].f+=minf; maxflow+=minf*e[v].c; } } void init() { maxflow=0; s=n*n*2; t=s+1; en=0; memset(head,-1,sizeof(head)); } bool ok(int x,int y) { return x>=1&&x<=n&&y>=1&&y<=n; } void build() { for(int i=1,id=0;i<=n;i++) for(int j=1;j<=n;++j,id++) { add(id*2,id*2+1,-map[i][j],1); add(id*2,id*2+1,0,INF); if(/*ok(i,j+1)*/ j+1<=n) add(id*2+1,(id+1)*2,0,INF); if(/*ok(i+1,j)*/ i+1<=n) add(id*2+1,(id+n)*2,0,INF); } add(s,0,0,k); add(s-1,t,0,k); } int main() { int i,j,b; while(scanf("%d%d",&n,&k)!=EOF) { init(); for(i=1;i<=n;i++) for(j=1;j<=n;++j) scanf("%d",&map[i][j]); build(); while(spfa()) add(); printf("%d\n",-maxflow); } return 0; }