Codeforces - 1102F - Elongated Matrix(建图 + 哈密顿通路)

题目链接:https://codeforces.com/problemset/problem/1102/F

15.1 题意

给你一个 n ( 1 ≤ n ≤ 16 ) n(1 \le n \le 16) n(1n16) m ( 1 ≤ m ≤ 1 0 4 ) m(1 \le m \le 10^4) m(1m104) 列的矩阵 a ( 1 ≤ a i , j ≤ 1 0 9 ) a(1 \le a_{i,j} \le 10^9) a(1ai,j109),现在你可以任意交换行的位置,但是列的位置不能交换。

之后你按照第一列从上往下、第二列从上往下…的顺序遍历这个矩阵,设遍历序列为 s 1 , s 2 , … , s n m s_1, s_2, \dots, s_{nm} s1,s2,,snm

一个遍历是 k − k- k可接受的,当且仅当对于任意的 i ∈ [ i , n m − 1 ] i\in [i,nm-1] i[i,nm1],满足 ∣ s i − s i + 1 ∣ ≥ k |s_i-s_{i+1}| \ge k sisi+1k

找到最大的合法的 k k k

15.2 解题过程

先考虑最暴力的做法:暴力枚举所有行的交换情况,判定合法性并计算 k k k 值,贡献到答案中。

因为 n = 16 n=16 n=16,所以这种暴力的做法是无法接受的。

考虑建图,对行 i i i 和行 j j j 建边,边权为这两行相邻(不考虑首尾)时的 k k k 值,设这一值为 l e n g t h [ i ] [ j ] length[i][j] length[i][j]

之后,计算 i i i j j j 作为首尾时产生的 k k k u p d o w n [ i ] [ j ] updown[i][j] updown[i][j]

这时,问题转化为枚举 i i i j j j 行,求以 i i i 为起点, j j j 为终点的最大哈密顿通路。

考虑状压 dp,设 d p [ k ] [ i ] [ j ] dp[k][i][j] dp[k][i][j] 为边的加入情况为 k k k,从 i i i 行出发,终点为 j j j 行的最大价值。

转移时,先不考虑 u p d o w n [ i ] [ j ] updown[i][j] updown[i][j] 对答案的贡献,只考虑 l e n g t h [ i ] [ j ] length[i][j] length[i][j] 进行转移。

最后的时候,再将 u p d o w n [ i ] [ j ] updown[i][j] updown[i][j] 的贡献加入。

时间复杂度: O ( 2 n ⋅ n 3 ) O(2^{n} \cdot n^3) O(2nn3)

15.3 代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 10005;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll matrix[20][MAXN];
ll length[20][20];
ll updown[20][20];
ll dp[(1 << 16)][20][20];
bool isconn[(1 << 16)][20][20];
int main(void)
{
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            scanf("%I64d", &matrix[i][j]);
        }
    }
    if(n == 1)
    {
        ll answer = inf;
        for(int i = 1; i < m; i++)
            answer = min(answer, abs(matrix[0][i] - matrix[0][i - 1]));
        printf("%I64d\n", answer);
        return 0;
    }
    memset(isconn, false, sizeof(isconn));
    for(int i = 0; i < n; i++)
        isconn[(1 << i)][i][i] = true;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            if(i == j)
            {
                length[i][j] = 0;
                continue;
            }
            ll temp = inf;
            for(int k = 0; k < m; k++)
            {
                temp = min(temp, abs(matrix[i][k] - matrix[j][k]));
            }
            length[i][j] = temp;
            temp = inf;
            for(int k = 1; k < m; k++)
                temp = min(temp, abs(matrix[i][k] - matrix[j][k - 1]));
            updown[i][j] = temp;
        }
    }
    for(int i = 0; i < (1 << n); i++)
    {
        int cnt = 0;
        int S = i;
        while(S)
        {
            if(S % 2)
                ++cnt;
            S /= 2;
        }
        for(int j = 0; j < n; j++)
        {
            for(int k = 0; k < n; k++)
            {
                if(!isconn[i][j][k])
                    continue;
                for(int l = 0; l < n; l++)
                {
                    if(i & (1 << l))
                        continue;
                    int dest = (i + (1 << l));
                    isconn[dest][j][l] = true;
                    if(cnt == 1)
                        dp[dest][j][l] = max(dp[dest][j][l], length[k][l]);
                    else
                        dp[dest][j][l] = max(dp[dest][j][l], min(dp[i][j][k], length[k][l]));
                }
            }
        }
    }
    ll answer = 0;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            if(i == j)
                continue;
            dp[(1 << n) - 1][i][j] = min(dp[(1 << n) - 1][i][j], updown[i][j]);
            answer = max(answer, dp[(1 << n) - 1][i][j]);
        }
    }
    printf("%I64d\n", answer);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值