洛谷 P1777 幫助_NOI導刊2010提高(03) 解題報告
阿新 • • 發佈:2018-07-06
ems 混亂 取出 ring ++ 數據 inline 決定 memset
P1777 幫助_NOI導刊2010提高(03)
題目描述
Bubu的書架亂成一團了!幫他一下吧!
他的書架上一共有n本書。我們定義混亂值是連續相同高度書本的段數。例如,如果書的高度是30,30,31,31,32,那麽混亂值為3,30,32,32,31的混亂度也是3,但31,32,31,32,31的混亂度是5-,這實在是太亂了。
Bubu想盡可能地減少混亂度,但他有點累了,所以他決定最多取出k本書,再隨意將它們放到書架上。你能幫助他嗎?
輸入輸出格式
輸入格式:
最多會有20組測試數據。每組測試數據開頭為兩個整數n,k(l≤k≤n≤100),表示總共有n本書,最多可以進行k次搬書操作。接下來一行有n個整數,表示每本書的高度,從左到右。每本書的高度是25到32間的整數。最後一組數據後有一行n=k=0。
輸出格式:
對於每一組數據,輸出Case標號和最終最小的混亂度。在每組數據後打印一個空行。
我們發現,對於拿出去的書是可以隨便放的,因為我們可以最後處理它們。
很顯然要做DP,順序可以直接從左到右,滿足無後效性。需要最後一個書的編號,以便後面使用。當然取出了幾本也得壓進狀態。
為了處理拿出去的書,我們得把書的狀態集合給存儲下來。因為拿出去的書拿走以後,可能在原書架裏面沒有了,最後要根據狀態放回來。
方程:
\(dp[i][j][sta][l]\)表示\(i\)位置第\(j\)次換書之前的書的狀態集合為\(sta\)當前書的集合最後一本為\(l\)
Code:
#include <cstdio> #include <cstring> const int N=104; const int inf=0x3f3f3f3f; int min(int x,int y){return x<y?x:y;} int dp[2][N][1<<10][10],n,k,r,a[N]; void DP() { for(int i=1;i<=n;i++)//位置 { memset(dp[i&1],0x3f,sizeof(dp[i&1])); for(int j=0;j<=min(k,i);j++)//拿走了幾個 for(int sta=0;sta<(1<<r);sta++)//前面的狀態,這個沒填 for(int l=0;l<=r;l++)//末尾書本 if(!l||(sta>>l-1)&1) { dp[i&1][j][sta|(1<<a[i]-1)][a[i]]=min(dp[i&1][j][sta|(1<<a[i]-1)][a[i]] ,dp[i-1&1][j][sta][l]+(l!=a[i]));//不拿 if(j) dp[i&1][j][sta][l]=min(dp[i&1][j][sta][l],dp[i-1&1][j-1][sta][l]);//拿走 } } } int cal(int x) { int cnt=0; while(x) { cnt++; x-=x&-x; } return cnt; } int main() { scanf("%d%d",&n,&k); int cnt=0; while(n&&k) { int sta=0; r=0;cnt++; for(int i=1;i<=n;i++) { scanf("%d",a+i); a[i]-=24; r=r>a[i]?r:a[i]; sta|=1<<a[i]-1; } memset(dp[0],0x3f,sizeof(dp[0])); dp[0][0][0][0]=0; DP(); int ans=inf; for(int i=1;i<=k;i++) for(int j=0;j<(1<<r);j++) for(int l=1;l<=r;l++) if(dp[n&1][i][j][l]!=inf) ans=min(ans,dp[n&1][i][j][l]+cal(sta-j)); printf("Case %d: %d\n\n",cnt,ans); scanf("%d%d",&n,&k); } return 0; }
2018.7.6
洛谷 P1777 幫助_NOI導刊2010提高(03) 解題報告