思路:这题应该可以比较容易知道是最小费用最大流了,因为每个棋盘点就是一个费用,然后第k次正好是网络流量,这样就可以知道了。不过因为要求的是第k次最大的,所以是最大费用最大流,我们只需把费用变成负的,就可以变成求最小费用最大流了嘛!求出的花销是负数,取正就是答案了!
但是因为题目没有直接给边,所以我们需要自己建边。这是我觉得应该是最容易的拆点建图了吧!把每个点拆成两个点,这两个点之间的边费用就是这个矩阵点的值了,那么流量当然就是1咯,因为k是流量嘛,这条边只能用一次,所以流量当然是1,费用为-a[i][j],但是因为这条边连接了下面图的其他边,所以这条边也需要用k次,所以再把这条边的流量和费用改一下,所以流量为k-1,费用为0;然后矩阵点与点之间再连边(因为向右和向下连两边),因为这个没有费用,所以为0,而这条边可能要用k次,所以流量为k。然后建立超级源点和超级汇点流量为0,费用为k。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<queue> #include<set> #include<cmath> #include<bitset> #define mem(a,b) memset(a,b,sizeof(a)) #define lson i<<1,l,mid #define rson i<<1|1,mid+1,r #define llson j<<1,l,mid #define rrson j<<1|1,mid+1,r #define INF 0x7fffffff typedef long long ll; typedef unsigned long long ull; using namespace std; #define maxn 20005 struct { int v,w,c,next,re; //re记录逆边的下标,c是费用,w是流量 } e[maxn]; int n,cnt; int head[maxn],que[maxn*8],pre[maxn],dis[maxn]; bool vis[maxn]; void add(int u, int v, int w, int c) { e[cnt].v=v,e[cnt].w=w,e[cnt].c=c; e[cnt].next=head[u]; e[cnt].re=cnt+1,head[u]=cnt++; e[cnt].v=u,e[cnt].w=0,e[cnt].c=-c; e[cnt].next=head[v]; e[cnt].re=cnt-1,head[v]=cnt++; } bool spfa() { int i, l = 0, r = 1; for(i = 0; i <= n; i ++) dis[i] = INF,vis[i] = false; dis[0]=0,que[0]=0,vis[0]=true; while(l<r) { int u=que[l++]; for(i=head[u];i!=-1;i=e[i].next) { int v = e[i].v; if(e[i].w&&dis[v]>dis[u]+e[i].c) { dis[v] = dis[u] + e[i].c; pre[v] = i; if(!vis[v]) { vis[v] = true; que[r++] = v; } } } vis[u] = false; } return dis[n]!=INF; } int change() { int i,p,sum=INF,ans=0; for(i=n;i!=0;i=e[e[p].re].v) {//e[e[p].re].v为前向结点,不理解看add和spfa p=pre[i];//p为前向结点编号 sum=min(sum,e[p].w); } for(i=n;i!=0;i=e[e[p].re].v) { p=pre[i]; e[p].w-=sum; e[e[p].re].w+=sum; ans+=sum*e[p].c;//c记录的为单位流量费用,必须得乘以流量。 } return ans; } int EK() { int sum=0; while(spfa()) sum+=change(); return sum; } void init() { mem(head,-1),mem(pre,0),cnt=0; } int a[51][51],b[51][51],tmp; int main() { //freopen("1.txt","r",stdin); int k,i,j; init(); scanf("%d%d",&n,&k); for(i=0;i<n;i++) for(j=0;j<n;j++) { scanf("%d",&a[i][j]); b[i][j]=++tmp; add(b[i][j]*2-1,b[i][j]*2,1,-a[i][j]); add(b[i][j]*2-1,b[i][j]*2,k-1,0); } for(i=0;i<n;i++) for(j=0;j<n-1;j++) add(b[i][j]*2,b[i][j+1]*2-1,k,0); for(i=0;i<n-1;i++) for(j=0;j<n;j++) add(b[i][j]*2,b[i+1][j]*2-1,k,0); add(0,1,k,0); add(tmp*2,tmp*2+1,k,0); n=tmp*2+1; printf("%d\n",-EK()); return 0; }