题意:给出$N$个数和一个长为$M$、所有数在$[1,N]$范围之内的正整数序列$a_i$,求出这$N$个数的一种排列$p_1...p_N$使得$\sum\limits_{i=2}^M |p_{a_i}-p_{a_{i-1}}|$最小。$N \leq 20,M \leq 1000$
$N \leq 20$给了我们很明显的状压DP的信息,但是DP方程的思维难度还是有点大。
我们考虑按照数字从小到大地指定它在$p_i$中的位置。这样我们可以通过预处理某一个位置在$a_i$中相邻位置的数字的情况得到这一个数的贡献(也就是可以直接把绝对值符号拆开来计算它的贡献),这样子转移就会方便很多了。
看到绝对值就要考虑一下从小到大然后拆掉绝对值符号算贡献呢qwq
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int now[21] , dp[1 << 21] , cnt[21][1 << 21] , cnt1[1 << 21] , N , M; 5 6 inline int lowbit(int x){ 7 return x & -x; 8 } 9 10 int main(){ 11 cin >> N >> M; 12 for(int i = 0 ; i < N ; i++) 13 cin >> now[i]; 14 int last = -1; 15 while(M--){ 16 int x; 17 cin >> x; 18 x--; 19 if(last != -1){ 20 cnt[last][1 << x]++; 21 cnt[x][1 << last]++; 22 } 23 last = x; 24 } 25 memset(dp , 0x3f , sizeof(dp)); 26 dp[0] = 0; 27 sort(now , now + N); 28 for(int i = 1 ; i < 1 << N ; i++) 29 for(int j = 0 ; j < N ; j++) 30 cnt[j][i] = cnt[j][i - lowbit(i)] + cnt[j][lowbit(i)]; 31 for(int i = 1 ; i < 1 << N ; i++){ 32 cnt1[i] = cnt1[i - lowbit(i)] + 1; 33 for(int j = 0 ; j < N ; j++) 34 if(i & (1 << j)) 35 dp[i] = min(dp[i] , dp[i ^ (1 << j)] + (cnt[j][i] - cnt[j][(1 << N) - 1 - i]) * now[cnt1[i] - 1]); 36 } 37 cout << dp[(1 << N) - 1]; 38 return 0; 39 }