题意:给出n和k,表明书架上有n本书,可以取出k次书,每次取出后可以任意放回书架中,书的编号为25~32八种,定义该书架的混乱程度为片段的个数。求出最小的混乱度。
分析:看到题完全没什么头绪的样子,看了题解,这种略为复杂的状压DP的状态表示比起平时做DP题直接找一个切入点去建模YY状态的表示方法和状态转移方程,更像是直接对暴力搜索进行抽象后的得到的一个可行的状态表示方法,DP之所以效率高就是因为它的状态表示对问题进行了极大的抽象,把大部分中间情况压入了一个状态当中,然后利用无后效性逐步递推得到每种状态的结果。f[i][j][s][end]表示当前处理到了第i本书,前边一共拿走了j本,剩下的书的状态为s,最后一本书为end的最小混乱度,本题还有很重要的一点就是,我们拿走一本书后可以先不考虑放回,最后全部拿完后再一次全部放回,然后是对于每个本书i的策略:如果i和end同种,那么f[i][j][s][end] = f[i-1][j][s][end],否则拿走:f[i][j+1][s][end] = f[i-1][j][s][end]+1,不拿走f[i][j][s | col[i]][col[i]] = f[i-1][j][s][end]+1,边界条件f[0][0][0][0] = 0;
#include <queue>
#include <vector>
#include <cstdio>
#include <utility>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int n,k,tot,toss,f[2][105][300][10],col[105];
int got(int x)
{
int now = x ^ tot,num = 0;
while(now)
{
if(now & 1) num++;
now>>=1;
}
return num;
}
int main()
{
cin.sync_with_stdio(false);
while(cin>>n>>k && n+k)
{
tot = 0;
for(int i = 1;i <= n;i++)
{
cin>>col[i];
col[i]-=24;
tot = tot | (1<<(col[i]-1));
}
memset(f[0],INF,sizeof(f[0]));
f[0][0][0][0] = 0;
for(int i = 0;i < n;i++)
{
int now = i & 1;
memset(f[now^1],INF,sizeof(f[now^1]));
for(int j = 0;j <= k;j++)
for(int s = tot;s >= 0;s = min(s-1,(s-1)&tot))
for(int End = 0;End <= 8;End++)
{
if(col[i+1] == End) f[now^1][j][s][End] = min(f[now^1][j][s][End],f[now][j][s][End]);
else
{
f[now^1][j+1][s][End] = min(f[now^1][j+1][s][End],f[now][j][s][End]);
f[now^1][j][s | (1<<(col[i+1]-1))][col[i+1]] = min(f[now^1][j][s | (1<<(col[i+1]-1))][col[i+1]],f[now][j][s][End]+1);
}
}
}
int ans = 2147483647;
for(int j = 0;j <= k;j++)
for(int s = tot;s >= 0;s = min(s-1,(s-1)&tot))
for(int End = 0;End <= 8;End++)
ans = min(ans,f[n & 1][j][s][End] + got(s));
cout<<"Case "<<++toss<<": "<<ans<<endl<<endl;
}
}