1. 程式人生 > >洛谷 P1777 幫助_NOI導刊2010提高(03) 解題報告

洛谷 P1777 幫助_NOI導刊2010提高(03) 解題報告

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) 解題報告