题目链接:https://codeforces.com/problemset/problem/1102/F
15.1 题意
给你一个 n ( 1 ≤ n ≤ 16 ) n(1 \le n \le 16) n(1≤n≤16) 行 m ( 1 ≤ m ≤ 1 0 4 ) m(1 \le m \le 10^4) m(1≤m≤104) 列的矩阵 a ( 1 ≤ a i , j ≤ 1 0 9 ) a(1 \le a_{i,j} \le 10^9) a(1≤ai,j≤109),现在你可以任意交换行的位置,但是列的位置不能交换。
之后你按照第一列从上往下、第二列从上往下…的顺序遍历这个矩阵,设遍历序列为 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,nm−1],满足 ∣ s i − s i + 1 ∣ ≥ k |s_i-s_{i+1}| \ge k ∣si−si+1∣≥k。
找到最大的合法的 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(2n⋅n3)。
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;
}