Bubu’s bookshelf is in a mess! Help him!
There are nbooks on his bookshelf. We define the mess value to be the number of segments of
consecutive equal-height books. For example, if the book heights are 30, 30, 31, 31, 32, the mess
value is 3, that of 30, 32, 32, 31 is also 3, but the mess value of 31, 32, 31, 32, 31 is 5 — it’s indeed in
a mess!
Bubu wants to reduce the mess value as much as possible, but he’s a little bit tired, so he decided
to take out at most kbooks, then put them back somewhere in the shelf. Could you help him?
书架上有 N 本书,书本高度是 [25cm,32cm],凌乱地排着。
定义一排书的整齐度是指最少可以被划分成高度连续相等的书的段数。
比如 {30,32,32,31} 整齐度是 3。
比如 {31,32,31,32,31} 整齐度是 5。
现在最多从其中拿出 K 本书,然后塞回去。
问整理后,最少可以有多少段高度相等的连续区间。
K <= N <= 100
发现书本高度最大最小差为8,很容易想到状压dp。
设 \(dp(i, j, k, o)\) 表示前 \(i\) 本书里拿出 \(j\) 本,剩下书高度集合为 \(k\),且最后一本书高度为 \(o\) 的最小凌乱度。
然后枚举每一本书拿或者不拿。
如果不拿,则 \(k\) 变成 \(k|(1<<a_i)\),代表这本书放入未被拿的集合。\(o\) 变成 \(a_i\)。如果之前 \(o\neq a_i\),则要加1。
如果拿,剩下书的集合还有最后一本不变。然后我们考虑是否+1,如果为拿出的集合中有这本书,我们可以把现在这本书插到它旁边,然后不用+1。如果后面还有和它一样的书,我们就把现在这本书的命运交给后面那本书。除了这两种情况,其它都要+1。
可能会MLE,要滚动数组优化。
// Problem: UVA12235 Help Bubu // Contest: Luogu // URL: https://www.luogu.com.cn/problem/UVA12235 // Memory Limit: 0 MB // Time Limit: 3000 ms // // Powered by CP Editor (https://cpeditor.org) #include<bits/stdc++.h> using namespace std; //#define int long long inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+ (x<<3)+(ch^48);ch=getchar();}return x*f;} //#define M //#define mo #define N 110 int n, m, i, j, k; int ans, dp[2][N][300][10]; int is[1010][10], a[1010]; int o, nw, ls, K, t; void minn(int &x, int y) { x=min(x, y); } signed main() { // freopen("tiaoshi.in", "r", stdin); // freopen("tiaoshi.out", "w", stdout); scanf("%d%d", &n, &K); while(n) { memset(dp, 0x3f, sizeof(dp)); memset(is, 0, sizeof(is)); for(i=1; i<=n; ++i) a[i]=read()-25; // for(i=1; i<=n; ++i) printf("%d ", a[i]); // printf("\n"); for(i=1; i<=n; ++i) for(j=i+1; j<=n; ++j) is[i][a[j]]=1; ans=dp[0][0][0][0]; dp[0][0][0][8]=0; for(i=1; i<=n; ++i) { nw=i&1; ls=(!nw); memset(dp[nw], 0x3f, sizeof(dp[nw])); for(j=0; j<=K; ++j) for(k=0; k<=255; ++k) for(o=0; o<=8; ++o) if(o==8||((1<<o)&k))//之前未选集合中含有最后一个 { minn(dp[nw][j][k|(1<<a[i])][a[i]], dp[ls][j][k][o]+(a[i]!=o)); if(j) minn(dp[nw][j][k][o], dp[ls][j-1][k][o]+( (((1<<a[i])&k)||(is[i][a[i]])) ? 0 : 1 ) ); } } for(j=0; j<=K; ++j) for(k=0; k<=255; ++k) for(o=0; o<=8; ++o) if(o==8||((1<<o)&k)) ans=min(ans, dp[n&1][j][k][o]); printf("Case %d: %d\n\n", ++t, ans); scanf("%d%d", &n, &K); } return 0; }