5 2 25 25 32 32 25 5 1 25 26 25 26 25 0 0
Case 1: 2 Case 2: 3
题意: 给你n本书,书的高度有8种(这个是重点)。然后你可以最多有m次操作,每次的操作,就是拿出一本书放到任意位置,最后求出最小的连续相同高度的段数。
题解:滚动数组+状态压缩
开始的时候想到了记录前i个,取j次和最后的书的高度s,但是怎么都想不出这么转移。后来看了别人的题解来知道还要有记录当前高度状态k。具体的可以看代码中的注释。
#include<cstdio> #include<cstring> using namespace std; //dp[i][j][k][s],前i个位置,花费j次操作,k为为未拿出书的集合,最后一本书的高度为s //不取走第i位 1.s==tall[i] dp[i][j][k][s]=min(dp[i][j][k][s],dp[i-1][j][k][s] // 2.s!=tall[i] dp[i][j][k|(1<<tall[i])][tall[i]]=min(dp[i][j][k|(1<<tall[i])][tall[i]],dp[i-1][j][k][s]+1) //取走第i位 dp[i][j+1][k][s]=min(dp[i][j+1][k][s],dp[i-1][j][k][s]) #define min(a,b) ((a)<(b)?(a):(b)) #define inf ((1<<30)-1) int dp[2][105][1<<8][10]; int tall[105],num[1<<8]; int main() { int n,m,begin; //预处理 memset(num,0,sizeof(num)); for(int i=0; i<(1<<8); ++i) for(int j=0; j<8; ++j) if(i&(1<<j)) num[i]++; for(int cas=1;~scanf("%d%d",&n,&m);++cas) { if(n+m==0) break; begin=0;//初始高度状态 for(int i=1; i<=n; ++i) { scanf("%d",&tall[i]); tall[i]-=25; begin=begin|(1<<tall[i]); } for(int i=0; i<=m; ++i)//取走的次数 for(int j=0; j<(1<<8); ++j)//高度状态 for(int k=0; k<=8; ++k)//最后的书的高度 dp[0][i][j][k]=inf; dp[0][0][0][8]=0;//起始点 for(int i=1; i<=n; ++i) { for(int j=0; j<=m; ++j) for(int k=0; k<(1<<8); ++k) for(int s=0; s<=8; ++s) dp[i&1][j][k][s]=inf; for(int j=0; j<i&&j<=m; ++j) for(int k=0; k<(1<<8); ++k) for(int s=0; s<=8; ++s) if(dp[(i-1)&1][j][k][s]!=inf) { //取走第i位 if(j<m) dp[i&1][j+1][k][s]=min(dp[i&1][j+1][k][s],dp[(i-1)&1][j][k][s]); //不取走第i位且高度与前一个相同 if(s==tall[i]) dp[i&1][j][k][s]=min(dp[i&1][j][k][s],dp[(i-1)&1][j][k][s]); //不取走第i位且高度与前一个不同 else dp[i&1][j][k|(1<<tall[i])][tall[i]]=min(dp[i&1][j][k|(1<<tall[i])][tall[i]],dp[(i-1)&1][j][k][s]+1); } } int ans=inf; for(int i=0; i<=m; ++i) for(int j=0; j<(1<<8); ++j) for(int k=0; k<8; ++k) if(dp[n&1][i][j][k]!=inf) { int temp=j^begin;//不在此状态且拿出的书的高度 if(ans>dp[n&1][i][j][k]+num[temp]) ans=dp[n&1][i][j][k]+num[temp]; } printf("Case %d: %d\n\n",cas,ans); } return 0; }