題目連結:http://www.lydsy.com/JudgeOnline/problem.php?id=1231
題意:
給你n個數字s[i],問你有多少個排列,使得任意相鄰兩數字之差的絕對值大於m。
題解:
表示狀態:
dp[i][j][state] = arrangements
i:考慮到第i個位置。
j:上一個數字是s[j]。(j = n表示沒有上一個數字)
state:表示哪些數字已經被選過。
找出答案:
ans = ∑ dp[n][j][(1<<n)-1]
如何轉移:
now: dp[i][j][state]
列舉第i個位置要放數字s[k]。
dp[i+1][k][state|(1<<k)] += dp[i][j][state]
轉移條件:
(1)abs(s[j]-s[k])>m || j==n
與上一個數字之差的絕對值 > m,或沒有上一個數字。
(2)!((state>>k)&1)
數字s[k]還沒被選過。
邊界條件:
dp[0][n][0] = 1
others = 0
優化:
因為dp要用long long存,空間正好爆了。。。
第一維改成滾動陣列。
注意:當前為dp[i&1],要用dp[(i+1)&1],要把dp[(i+1)&1]全部設為0。
即:memset(dp[(i+1)&1],0,sizeof(dp[(i+1)&1]))
AC Code:
1 // state expression: 2 // dp[i][j][state] = arrangements 3 // i: considering ith pos 4 // j: last cow 5 // state: state of selection 6 // 7 // find the answer: 8 // sigma dp[n][j][(1<<n)-1] 9 // 10 // transferring: 11 // now: dp[i][j][state] 12 // dp[i+1][k][state|(1<<k)] += dp[i][j][state] 13 // abs(s[j]-s[k])>m || j==n 14 // !((state>>k)&1) 15 // 16 // boundary: 17 // dp[0][n][0] = 1 18 // others = 0 19 #include <iostream> 20 #include <stdio.h> 21 #include <string.h> 22 #include <stdlib.h> 23 #define MAX_N 17 24 #define MAX_S 65540 25 26 using namespace std; 27 28 int n,m; 29 int s[MAX_N]; 30 long long ans=0; 31 long long dp[2][MAX_N][MAX_S]; 32 33 void read() 34 { 35 cin>>n>>m; 36 for(int i=0;i<n;i++) 37 { 38 cin>>s[i]; 39 } 40 } 41 42 void solve() 43 { 44 memset(dp,0,sizeof(dp)); 45 dp[0][n][0]=1; 46 for(int i=0;i<n;i++) 47 { 48 memset(dp[(i+1)&1],0,sizeof(dp[(i+1)&1])); 49 for(int j=0;j<=n;j++) 50 { 51 for(int state=0;state<(1<<n);state++) 52 { 53 if(dp[i&1][j][state]) 54 { 55 for(int k=0;k<n;k++) 56 { 57 if((abs(s[j]-s[k])>m || j==n) && !((state>>k)&1)) 58 { 59 dp[(i+1)&1][k][state|(1<<k)]+=dp[i&1][j][state]; 60 } 61 } 62 } 63 } 64 } 65 } 66 for(int i=0;i<n;i++) 67 { 68 ans+=dp[n&1][i][(1<<n)-1]; 69 } 70 } 71 72 void print() 73 { 74 cout<<ans<<endl; 75 } 76 77 int main() 78 { 79 read(); 80 solve(); 81 print(); 82 }