1. 程式人生 > >Codeforces Round #531 (Div. 3) F. Elongated Matrix(狀壓DP)

Codeforces Round #531 (Div. 3) F. Elongated Matrix(狀壓DP)

F. Elongated Matrix

題目連結:https://codeforces.com/contest/1102/problem/F

題意:

給出一個n*m的矩陣,現在可以隨意交換任意的兩行,最後從上到下,從左到右形成一個序列s1,s2.....snm,滿足對於任意相鄰的兩個數,它們差的絕對值的最大值為k。

現在問怎麼交換行與行,可以使得最後的這個k最大。

 

題解:

人生中第一道狀壓dp~其實還是參考了這篇部落格:https://blog.csdn.net/CSDNjiangshan/article/details/86239183?tdsourcetag=s_pctim_aiomsg

這篇部落格思路已經說得很清楚了,我就說下注意的幾點吧:

行和列的下標是從0開始的,這是為了適應二進位制操作,結合程式碼想想就知道了,我一開始就是這裡沒注意;

還有就是n=1時的特判,程式碼不能很好地處理這種情況,這時橫座標是0,不是1....

其餘的照著思路寫就是了。

我還有一個謎之問題,就是我先列舉中間點,比後列舉中間點要慢200.300ms左右,不知道為什麼,希望有大佬能幫我解答一下。

 

程式碼如下:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef 
long long ll; const int N = 16 , M = 1e4+5; int a[N][M]; int dp[N][N][1<<N]; int n,m; int dis[N][N],dis_next[N][N]; int main(){ scanf("%d%d",&n,&m); for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%d",&a[i][j]); memset(dis,INF,sizeof(dis)); memset(dis_next,INF,
sizeof(dis_next)); memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(i==j) continue ; for(int k=0;k<m;k++) dis[i][j]=min(dis[i][j],abs(a[i][k]-a[j][k])); for(int k=0;k<m-1;k++) dis_next[i][j]=min(dis_next[i][j],abs(a[i][k+1]-a[j][k])); } } int ans = 0; if(n==1){ ans = INF; for(int i=0;i<m-1;i++) ans=min(ans,abs(a[0][i]-a[0][i+1])); printf("%d",ans); return 0; } for(int l=0;l<n;l++) dp[l][l][1<<l]=INF; for(int l=1;l<(1<<n);l++){ for(int i=0;i<n;i++){ if((l>>i)&1==0) continue ; for(int j=0;j<n;j++){ if((l>>j)&1 || i==j) continue ; for(int k=0;k<n;k++){ if((l>>j)&1==0) continue ; int now = l|(1<<j); dp[i][j][now]=max(dp[i][j][now],min(dp[i][k][l],dis[k][j])); } } } } int now = (1<<n)-1; for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(i==j) continue ; ans=max(ans,min(dp[i][j][now],dis_next[i][j])); } } printf("%d",ans); return 0; }