【HDU 4862】Jump【KM】

15 篇文章 0 订阅
2 篇文章 0 订阅

题意:给出一个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();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值