刚开始是用记忆化搜索做的,貌似是贪心出了问题,各种WA。后来搜了下AC的代码,清一色的递推。刚开始看不懂,可能是因为什么dp都用记忆化搜索做的,显然,递推更考验思维而且时间复杂度比记忆化搜索低,以后得多用递推做题啊。
找了一道写的相对简单的代码,看懂了之后写,还T了几炮……
大概思路就是4维dp,dp[i][j][k][p]表示进行到第i本书,移动了j本书,k是已经摆放了的书的高度的集合,p是最后一本书的高度,dp值为此时分块的最小数量。
每本书分两种情况,移动或不移动。这里需要贪心一下,就是当之前已经摆放了x高度的书时,如果再移动x高度的书时就不可能再增加分块的数量了。所以,最终的结果就是dp值再加上状态k中未出现的高度的个数。
最后2.7s+水过了……
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int inf=1e9;
int dp[110][110][1<<8][9];
int n,k,m;
int a[110],vis[10],two[20];
int min(int a,int b){return a>b?b:a;}
int main()
{
int kase=1;
two[0]=1;
for(int i=1;i<=10;i++) two[i]=two[i-1]*2;
while(scanf("%d%d",&n,&k)!=EOF)
{
if(!n&&!k) break;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]-=24;
vis[a[i]]=1;
}
m=0;
for(int i=1;i<=9;i++)
{
if(vis[i])
{
m++;
for(int j=1;j<=n;j++) if(a[j]==i) a[j]=m;
}
}
int cnt=(1<<m)-1;
for(int i=0;i<=n;i++)
for(int j=0;j<=k;j++)
for(int p=0;p<=cnt;p++)
for(int q=0;q<=m;q++) dp[i][j][p][q]=inf;
dp[0][0][0][0]=0;
for(int i=0;i<n;i++)
for(int j=0;j<=k;j++)
for(int p=0;p<=cnt;p++)
for(int q=0;q<=m;q++) if(dp[i][j][p][q]!=inf)
{
dp[i+1][j][p|two[a[i+1]-1]][a[i+1]]=min(dp[i+1][j][p|two[a[i+1]-1]][a[i+1]],dp[i][j][p][q]+(q!=a[i+1]));
dp[i+1][j+1][p][q]=min(dp[i+1][j+1][p][q],dp[i][j][p][q]);
}
int ans=inf;
for(int i=0;i<=k;i++)
for(int j=0;j<=cnt;j++)
for(int p=0;p<=m;p++)
{
int tmp=0;
for(int q=1;q<=m;q++) if((j&two[q-1])==0) tmp++;
ans=min(ans,dp[n][i][j][p]+tmp);
}
printf("Case %d: %d\n\n", kase++, ans);
}
return 0;
}