题意:给出一个n×m的数字矩阵,给定一个k。表示你可以在这个矩阵上进行k次遍历,每一次遍历选择一个起点,遍历的方式只能向左或者向下移动。移动的代价如题目所述,如果目的地的数字与起始位置的数字相同,则获得数字的效益。求在k次遍历后是否能遍历整个矩阵,若能输出最大效益,否则输出-1.
题解:讲图变成二分图,X部和Y部的点数都为n×m+k,X部中的i点一步能到达Y部中的j点,则建一条权值为效益-代价,X部中的新添加的K个点与Y中前N*M个点建一条权值为0的点,相应的Y部中后K个点也是一样。这样表示每次遍历的起点的前驱和后继都是新建立的点,并且不会产生任何代价和效益。最后通过判断是否X部中的点都被匹配上就可以判断是否能遍历整个矩阵。最后的最优匹配就是答案。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500
bool xm[N], ym[N];
int w[N][N], A[N], B[N], rm[N];
// A左集合可行顶标, B为右集合可行顶标, rm[]为右集合点匹配左集合哪个点
// xv[] yv[] 分别表示左、右集合点是否在交错树中, 1是,0不是
int Hs, ms;
bool path(int a)//寻找可增广路
{
int b;
xm[a] = 1;//标记 Xi 在交错树中
for (b = 1;b <= ms;b++)
{
if (!ym[b] && A[a] + B[b] == w[a][b])//是否有 A[i] + B[j] = W[i][j]
{
ym[b] = 1;//标记 Yj 在交错树中
if (rm[b] == 0 || path(rm[b]))//j未被匹配 或者 可以找到可增广路
{
rm[b] = a;//匹配
return 1;
}
}
}
return 0;
}
void KM()
{
int i, j, sum = 0, d, k;
memset (B, 0, sizeof (B));
for (i = 1;i <= ms;i++)
{
A[i] = -INF;
for (j = 1;j <= ms;j++)
{
A[i] = max(A[i], w[i][j]);
}
}
memset (rm, 0, sizeof (rm));
for (i = 1;i <= ms;i++)
{
while (1)
{
memset (xm, 0, sizeof (xm));
memset (ym, 0, sizeof (ym));
if (path(i))
break;
d = INF;
for (k = 1;k <= ms;k++)
{
if (xm[k])
{
for (j = 1;j <= ms;j++)
{
if (!ym[j])
d = min(d, A[k] + B[j] - w[k][j]);
}
}
}
for (j = 1;j <= ms;j++)
{
if (xm[j])
A[j] -= d;
if (ym[j])
B[j] += d;
}
}
}
for (i = 1;i <= ms;i++) {
if (w[rm[i]][i] == -INF) break;
sum += w[rm[i]][i];
}
if (i <= ms) puts("-1");
else printf ("%d\n", sum);
}
int n, m;
char val[20][20];
int main()
{
int T, ca = 1, i, j, k, ii, jj;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m, &k);
for (i = 0;i < n;i++) {
scanf("%s", val[i]);
}
for (i = 1;i <= n*m+k;i++) {
for (j = 1;j <= n*m+k;j++) w[i][j] = -INF;
}
for (i = 1;i <= k;i++) w[i+n*m][i+n*m] = 0;
//for (i = 1;i <= n*m;i++)
// w[i][i] = 0;
for (i = 0;i < n;i++) {
for (j = 0;j < m;j++) {
for (ii = 0;ii < k;ii++) w[n*m+ii+1][i*m+j+1] = w[i*m+j+1][n*m+ii+1] = 0;
for (jj = j+1;jj < m;jj++) {
w[i*m+j+1][i*m+jj+1] = (val[i][j] == val[i][jj]?val[i][j]-'0':0)-(jj-j-1);
}
for (ii = i+1;ii < n;ii++) {
w[i*m+j+1][ii*m+j+1] = (val[i][j] == val[ii][j]?val[i][j]-'0':0)-(ii-i-1);
}
}
}
ms = n*m+k;
printf("Case %d : ", ca++);
KM();
}
}