题目链接: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 }