utpc2021I Card Decks 题解

  • ~1.53K 字

设状态为 fs,if_{s,i} 表示删掉了 ss 这个集合内的数,最后一个删掉的为 ii

转移是 jsj\notin s,计算吃掉 ii 后到要去吃 jj 要多少次操作,也就是他们中间有多少张牌。可以 O(nm3)O(nm^3) 预处理,在转移时 O(m)O(m) 计算贡献,总复杂度 O((n+2m)m3)O((n+2^m)m^3)

假设吃掉牌的顺序为 p1,p2,,pmp_1,p_2,\dots,p_m,注意到操作 11 的次数为 pi1p_{i-1}pip_i 下的 mim-i 的和。

感性理解一下,实际上需要得到一张牌被送入牌堆底的总次数,如果 pi1p_{i-1}pip_i 下,pip_ipi1p_{i-1} 的牌会在删 pi1p_{i-1} 这一轮被送到牌堆底,剩下的会在删 pip_i 这一轮都被送到牌堆底,被操作的数量是 mim-i。对于 pi1p_{i-1}pip_i 上的情况,pi1p_{i-1}pip_i 之间的牌会和某次删牌构成之前的情况从而被统计贡献。

O(m2)O(m^2) 预处理,总复杂度 O((n+2m)m2)O((n+2^m)m^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const int N = 22;
int f[1<<N][N];
int a[N],c[N][N];
int n,m;

int main(){
n = in,m = in;
for(int k=0;k<n;k++){
for(int j=0;j<m;j++){
a[j] = (int)in-1;
for(int i=0;i<j;i++)
c[a[i]][a[j]]++;
}
}
memset(f,0x3f,sizeof(f));
for(int k=0;k<m;k++)
f[1<<k][k] = 0;
for(int k=1;k<(1<<m);k++){
int w = __builtin_popcount(k);
for(int j=0;j<m;j++)
if((k>>j)&1)
for(int i=0;i<m;i++)
if(!((k>>i)&1))
f[k^(1<<i)][i] = min(f[k^(1<<i)][i],f[k][j]+(m-w)*c[i][j]);
}
int ans = 1e9;
for(int k=0;k<m;k++)
ans = min(ans,f[(1<<m)-1][k]);
cout << ans;
return 0;
}