题目链接
题意
给你一个n行m列的矩阵,矩阵可以行交换。
从上至下,从左到右,下标标记为
s
1
s_1
s1,
s
2
s_2
s2,
s
3
s_3
s3,
s
4
s_4
s4……
求
∣
s
i
−
s
i
−
1
∣
|s_i-s_{i-1}|
∣si−si−1∣最大
思路
预处理每行。
- 将每行变为一个点,每点之间距离就是,行内对应元素最小差值。
- 注意第一行(相对,由于可以交换)与最后一行(相对,由于可以交换),都需要另算,行内对应元素错位最小差值。
即求新建的完全图哈密顿回路,最小边最大。
首先哈密顿回路必定存在,我们可以枚举起点。
求以该点为起点的所有终点的哈密顿通路的答案tmp。
由于是回路,所以还要将答案tmp与起点终点的2进行比较取小。
对于每个枚举的起点,状压DP[i][j] i表示当前已经走过的点,j表示当前走到的最后一个点。
对于i状态,枚举他的两个子集,j,k,表示j是终点,k是起点。
dp[i][j] = max(dp[i][j], min(dp[i^(1<<j)][k], e[k][j]));
这样状压DP满足无后效性,for从0到(1<<n)-1,是从小到大
比如0110011这种状态,他的所有子状态都比他小,会先枚举到子状态,而他也不会更新子状态
数组开小了找了一天BUG,f**k。
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
int n, m, v[20][10005], e[20][20], dp[(1<<16)+5][20], e2[20][20];
inline ab(int a)
{
return a < 0 ? -a : a;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) scanf("%d",&v[i][j]);
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
{
e[i][j] = e2[i][j] = INF;
for(int k = 0; k < m; ++k) e[i][j] = min(e[i][j], ab(v[i][k]-v[j][k]));
for(int k = 1; k < m; ++k) e2[i][j] = min(e2[i][j], ab(v[i][k]-v[j][k-1]));
}
}
int ans = 0;
for(int st = 0; st < n; ++st)
{
memset(dp,0,sizeof(dp));
dp[1<<st][st] = INF;
for(int i = 0; i < (1<<n); ++i)
{
for(int j = 0; j < n; ++j)
{
if(i&(1<<j))
{
for(int k = 0; k < n; ++k)
{
if(j == k || ((i&(1<<k)) == 0)) continue; // 应该无所谓,反正0更新不了
dp[i][j] = max(dp[i][j], min(dp[i^(1<<j)][k], e[k][j]));
}
}
}
}
for(int en = 0; en < n; ++en) ans = max(ans, min(e2[st][en], dp[(1<<n)-1][en]));
}
printf("%d\n",ans);
return 0;
}