NewCoder110E Pocky游戏 状压DP

传送门

题意:给出$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 }

猜你喜欢

转载自www.cnblogs.com/Itst/p/9833372.html