以下是题解提供的网络流建图法。。
最小K路径覆盖的模型,用费用流或者KM算法解决,构造二部图,X部有N*M个节点,源点向X部每个节点连一条边,流量1,费用0,Y部有N*M个节点,每个节点向汇点连一条边,流量1,费用0,如果X部的节点x可以在一步之内到达Y部的节点y,那么就连边x->y,费用为从x格子到y格子的花费能量减去得到的能量,流量1,再在X部增加一个新的节点,表示可以从任意节点出发K次,源点向其连边,费用0,流量K,这个点向Y部每个点连边,费用0,流量1,最这个图跑最小费用最大流,如果满流就是存在解,反之不存在,最小费用的相反数就是可以获得的最大能量
以下是 某博客 http://blog.youkuaiyun.com/u010089558/article/details/38051555 提供的KM算法建图法
题解:讲图变成二分图,X部和Y部的点数都为n×m+k,X部中的i点一步能到达Y部中的j点,则建一条权值为效益-代价,X部中的新添加的K个点与Y中前N*M个点建一条权值为0的点,相应的Y部中后K个点也是一样。这样表示每次遍历的起点的前驱和后继都是新建立的点,并且不会产生任何代价和效益。最后通过判断是否X部中的点都被匹配上就可以判断是否能遍历整个矩阵。最后的最优匹配就是答案。
最后就是求最大匹配。。
KM:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MIN(a,b) ((a)<(b)?(a):(b))
#define INF 999999
#define MAX 400
int n,match[MAX];
bool sx[MAX],sy[MAX];
int lx[MAX],ly[MAX],map[MAX][MAX];
bool path(int u)
{
sx[u]=true;
for(int v=0;v<n;v++)
if(!sy[v]&&lx[u]+ly[v]==map[u][v])
{
sy[v]=true;
if(match[v]==-1||path(match[v]))
{
match[v]=u;
return true;
}
}
return false;
}
//true 是最大匹配 , false是最小匹配
int KM(bool truth)//可以不用更改地处理最小或最大权匹配
{
int i,j;
if(!truth)
{
for(i=0;i<n;i++)
for(j=0;j<n;j++)
map[i][j]=-map[i][j];
}
for(i=0;i<n;i++)
{
lx[i]=-INF;
ly[i]=0;
for(j=0;j<n;j++)
if(lx[i]<map[i][j])
lx[i]=map[i][j];
}
memset(match,-1,sizeof(match));
for(int u=0;u<n;u++)
while(1)
{
memset(sx,0,sizeof(sx));
memset(sy,0,sizeof(sy));
if(path(u)) break;
int dmin=INF;
for(i=0;i<n;i++)
if(sx[i])
for(j=0;j<n;j++)
if(!sy[j])
dmin=MIN(lx[i]+ly[j]-map[i][j],dmin);
for(i=0;i<n;i++)
{
if(sx[i])
lx[i]-=dmin;
if(sy[i])
ly[i]+=dmin;
}
}
int sum=0;
for(j=0;j<n;j++){
if(map[match[j]][j]==-INF)
return INF;
sum+=map[match[j]][j];
}
if(!truth)
{
sum=-sum;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
map[i][j]=-map[i][j];
}
return sum;
}
int N,M,K;
char s[30][30];
#define h1(x,y) (x*M+y)
void init()
{
n = N*M+K;
for(int i = 0;i < N;i++)
scanf("%s",s[i]);
for(int i = 0;i < n;i++)
for(int j = 0;j < n;j++)
map[i][j] = -INF;
for(int i = 0;i < K;i++) map[N*M+i][N*M+i] = 0;
for(int i = 0;i < N;i++)
for(int j = 0;j < M;j++)
{
for(int ii = 0;ii < K;ii++) map[N*M+ii][i*M+j] = map[i*M+j][N*M+ii] = 0;
for(int w = j+1;w < M;w++)
map[h1(i,j)][h1(i,w)] = (s[i][j]==s[i][w]?s[i][j]-'0':0)-(w-j-1);
for(int z = i+1;z < N;z++)
map[h1(i,j)][h1(z,j)] = (s[i][j]==s[z][j]?s[i][j]-'0':0)-(z-i-1);
}
}
int main()
{
int t,tt=0;
scanf("%d",&t);
while(t--)
{
scanf("%d %d %d",&N,&M,&K);
init();
int ans = KM(true);
printf("Case %d : ",++tt);
if(ans >= INF)
printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}
以下是费用流
#include <iostream>
#include <algorithm>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
int sumFlow;
const int MAXN = 502;
const int MAXM = 10002;
const int INF = 1000000000;
struct Edge
{
int u, v, cap, cost;
int next;
}edge[MAXM<<2];
int NE;
int head[MAXN], dist[MAXN], pp[MAXN];
bool vis[MAXN];
void init()
{
NE = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int cap, int cost)
{
edge[NE].u = u; edge[NE].v = v; edge[NE].cap = cap; edge[NE].cost = cost;
edge[NE].next = head[u]; head[u] = NE++;
edge[NE].u = v; edge[NE].v = u; edge[NE].cap = 0; edge[NE].cost = -cost;
edge[NE].next = head[v]; head[v] = NE++;
}
bool SPFA(int s, int t, int n)
{
int i, u, v;
queue <int> qu;
memset(vis,false,sizeof(vis));
memset(pp,-1,sizeof(pp));
for(i = 0; i <= n; i++) dist[i] = INF;
vis[s] = true; dist[s] = 0;
qu.push(s);
while(!qu.empty())
{
u = qu.front(); qu.pop(); vis[u] = false;
for(i = head[u]; i != -1; i = edge[i].next)
{
v = edge[i].v;
if(edge[i].cap && dist[v] > dist[u] + edge[i].cost)
{
dist[v] = dist[u] + edge[i].cost;
pp[v] = i;
if(!vis[v])
{
qu.push(v);
vis[v] = true;
}
}
}
}
if(dist[t] == INF) return false;
return true;
}
int MCMF(int s, int t, int n) // minCostMaxFlow
{
int flow = 0; // 总流量
int i, minflow, mincost;
mincost = 0;
while(SPFA(s, t, n))
{
minflow = INF + 1;
for(i = pp[t]; i != -1; i = pp[edge[i].u])
if(edge[i].cap < minflow)
minflow = edge[i].cap;
flow += minflow;
for(i = pp[t]; i != -1; i = pp[edge[i].u])
{
edge[i].cap -= minflow;
edge[i^1].cap += minflow;
}
mincost += dist[t] * minflow;
}
sumFlow = flow; // 题目需要流量,用于判断
return mincost;
}
char s[20][20];
int n,m,k;
int hash1(int i,int j){
return (i-1)*m+j;
}
int hash2(int i,int j){
return n*m+((i-1)*m+j);
}
int main()
{
int t,tt=0;
scanf("%d",&t);
while(t--){
init();
scanf("%d %d %d",&n,&m,&k);
int S = 0;
int T = hash2(n,m)+2;
int np = hash2(n,m)+1;
for(int i = 1;i <= n;i++)
scanf("%s",s[i]+1);
addedge(S,np,k,0);
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
addedge(S,hash1(i,j),1,0);
addedge(hash2(i,j),T,1,0);
addedge(np,hash2(i,j),1,0);
for(int z = j+1;z <= m;z++){
int cost = z - j - 1;
if(s[i][z] == s[i][j]) cost -= s[i][j]-'0';
addedge(hash1(i,j), hash2(i,z), 1, cost);
}
for(int k = i+1;k <= n;k++){
int cost = k - i - 1;
if(s[i][j] == s[k][j]) cost -= s[i][j]-'0';
addedge(hash1(i,j),hash2(k,j), 1 , cost);
}
}
}
printf("Case %d : ",++tt);
int ans=MCMF(S,T,T+1);
//printf("sumFlow=%d n=%d m=%d\n",sumFlow,n,m);
if(sumFlow==n*m) cout << -ans << endl;
else printf("-1\n");
}
return 0;
}