题意:HDU3237
n n n本书,每本书有自己的高度 x x x,一段连续的高度的书的混乱度为 1 1 1,不连续的高度的书混乱度为也 1 1 1,你有 k k k次操作移动某些书到任意位置,请问 k k k次操作后所有书的最小混乱值。
分析:
高度
25
25
25
→
\rightarrow
→
32
32
32即
0
0
0
→
\rightarrow
→
7
7
7 ,考虑状压压缩。
基本思想: 先取走书(也许是好几本一样的书, 一样的书取走之后肯定丢在一起), 最后再放回去
d
p
[
i
]
[
j
]
[
s
]
[
x
]
dp[i][j][s][x]
dp[i][j][s][x]表示前
i
i
i本书取走
j
j
j本书,剩下书的种类的状态为
s
s
s, 剩下书的最后一本书为
x
x
x。
它的值表示剩下的书的混乱度。
转移方程为:
设当前这本书为
x
x
x,上一本书为
p
p
p。
I
.
I.
I. 如果当前这本书与前一本书高度相同时
(
p
=
=
x
)
(p == x)
(p==x), 此时肯定不取走:
d
p
[
i
]
[
j
]
[
s
]
[
p
]
=
m
i
n
(
d
p
[
i
]
[
j
]
[
s
]
[
p
]
,
d
p
[
i
−
1
]
[
j
]
[
s
]
[
p
]
)
;
dp[i][j][s][p] = min(dp[i][j][s][p], dp[i - 1][j][s][p]);
dp[i][j][s][p]=min(dp[i][j][s][p],dp[i−1][j][s][p]);
I
I
.
II.
II. 如果当前这本书与前一本书高度不同时
(
p
!
=
x
)
(p != x)
(p!=x):
又要分两种情况:
1.
1.
1.不取走当前这本书
2.
2.
2.取走当前这本书
1.
d
p
[
i
]
[
j
]
[
s
∣
(
1
<
<
x
)
]
[
x
]
=
m
i
n
(
d
p
[
i
]
[
j
]
[
s
∣
(
1
<
<
x
)
]
[
x
]
,
d
p
[
i
−
1
]
[
j
]
[
s
]
[
p
]
+
1
)
;
1. dp[i][j][s | (1 << x)][x] = min(dp[i][j][s | (1 << x)][x], dp[i - 1][j][s][p] + 1);
1.dp[i][j][s∣(1<<x)][x]=min(dp[i][j][s∣(1<<x)][x],dp[i−1][j][s][p]+1);
2.
d
p
[
i
]
[
j
+
1
]
[
s
]
[
p
]
=
m
i
n
(
d
p
[
i
]
[
j
+
1
]
[
s
]
[
p
]
,
d
p
[
i
−
1
]
[
j
]
[
s
]
[
p
]
)
;
2. dp[i][j + 1][s][p] = min(dp[i][j + 1][s][p], dp[i - 1][j][s][p]);
2.dp[i][j+1][s][p]=min(dp[i][j+1][s][p],dp[i−1][j][s][p]);
因为我们求的是剩下的书的混乱度, 所以我们还要加上取走的书的混乱度。
枚举
d
p
[
n
]
[
k
]
[
0
−
>
m
a
x
S
t
a
t
e
]
[
0
−
>
b
i
t
(
m
a
x
S
t
a
t
e
)
]
dp[n][k][0 -> maxState][0 -> bit(maxState)]
dp[n][k][0−>maxState][0−>bit(maxState)]
然后计算每个状态的补集的1的个数, 就是我们要加上的取走的书的混乱度
最后
h
d
u
hdu
hdu上的这题卡内存, 所以滚动数组优化第一维即可.
写的时候还是有很多细节的…
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int maxn = 100 + 5;
const int maxm = (1 << 8) + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int n, x, k, cnt, ans, now, sta, cas, res, dp[2][maxn][maxm][10];
int main(){
while(~scanf("%d %d", &n, &k) && n + k){
memset(dp, inf, sizeof dp);
now = sta = 0;
for(int i = 0; i < n; i++){
scanf("%d", &x), x -= 25;
now ^= 1;
memset(dp[now], inf, sizeof dp[now]);
dp[now][i][1 << x][x] = 1;
for(int j = 0; j <= min(i, k); j++){//注意min(i, k)
for(int s = sta; s; s = (s - 1) & sta){//枚举子集
for(int p = 0; (1 << p) <= s; p++) if(dp[now ^ 1][j][s][p] != inf){
if(p == x){
dp[now][j][s][x] = min(dp[now][j][s][x], dp[now ^ 1][j][s][x]);
}else{
dp[now][j][s | (1 << x)][x] = min(dp[now][j][s | (1 << x)][x], dp[now ^ 1][j][s][p] + 1);
dp[now][j + 1][s][p] = min(dp[now][j + 1][s][p], dp[now ^ 1][j][s][p]);
}
}
}
}
sta |= (1 << x);
}
ans = inf;
for(int j = 0; j <= k; j++){
for(int s = sta; s; s = (s - 1) & sta){
for(int p = 0; (1 << p) <= s; p++) if(dp[now][j][s][p] != inf){
res = 0;
for(int ss = s ^ sta; ss; ss &= (ss - 1)) res++;
ans = min(ans, dp[now][j][s][p] + res);
}
}
}
printf("Case %d: %d\n\n", ++cas, ans);
}
return 0;
}