Luogu P1777 幫助 題解 [ 紫 ] [ 線性 dp ] [ 狀壓 dp ]

KS_Fszha發表於2024-08-04

幫助:大毒瘤!!!調了我2h,拍了我2h,最後沒調出來,重寫才AC。wdnmd。

思路

這題主要是線性 dp ,而狀壓 dp 只是最後在統計答案時的一個輔助。

首先定義 \(dp[i][j][k]\) 為考慮前 \(i\) 本書,已經抽出來了 \(j\) 本,最後沒被抽出來的一本書是高度 \(k\) 的最小混亂度。

每次放入的書與最後一位不同的時候我們把答案 \(+1\)

但是這樣有一個弊端,就是當我們取出一些書後,還要放回去。我們自然是要放到那些沒拿出來的同高度的書裡面,但是如果一種高度的書全拿出來了,我們就必須加上那些全拿出來的書的種數。

觀察到書本高度只能在 \([25,32]\) 這個區間裡,有 \(8\) 種高度,所以我們考慮把所有書的高度 \(-25\) 後狀壓。

重新定義 \(dp[i][j][k][l]\) 表示考慮前 \(i\) 本書,已經抽出來 \(j\) 本,此時沒被拿出來的書的狀態為 \(k\) ,放在書架裡的最後一本書的型別為 \(l\) 時的最小混亂度。

於是可以分兩種情況轉移:

  • 對於不把這本書拿出來的情況:

\[dp[i][j][k|(a[i])][a[i]]=\min(dp[i][j][k|(a[i])][a[i]],dp[i-1][j][k][l]+(a[i]!=l)) \]

這裡 \(k|(a[i])\) 的原因是要把它放進去。後面 \((a[i]!=l)\) 的原因是如果高度不同,混亂度要 \(+1\)

  • 對於把這本書拿出來的情況:

\[dp[i][j+1][k][l]=\min(dp[i][j+1][k][l],dp[i-1][j][k][l]) \]

在 dp 完成之後,我們最後列舉後三維,並且讓最終狀態 \(((st \oplus p)\&p)\) ,就可以求出書架裡少了哪些書,把少的書的個數加上即可,最後求個 \(\min\) 。注意提前預處理出每個二進位制數 \(1\) 的個數減小常數。

最後觀察到空間開銷大,並且每次轉移只會用到 \(i-1\) 裡的數,所以考慮滾動陣列最佳化 dp 。
同時注意提前把不合法的情況排除掉,可以減小部分常數。

程式碼

#include <bits/stdc++.h>
using namespace std;
int dp[2][105][260][15],a[105],pre[260],n,m,p,ans,now=0;
void solve()
{
	ans=0x3f3f3f3f;
	memset(dp,0x3f,sizeof(dp));
	dp[0][0][0][8]=0;
	for(int i=1;i<=n;i++)
	{
		int ni=(i&1),lst=(ni^1);
		memset(dp[ni],0x3f,sizeof(dp[ni]));
		for(int j=0;j<=m;j++)
		{
			for(int k=0;k<=p;k++)
			{
				for(int l=0;l<=8;l++)
				{
					if(dp[lst][j][k][l]>=(0x3f3f3f3f/2))continue;
					dp[ni][j][(k|(1<<a[i]))][a[i]]=min(dp[ni][j][(k|(1<<a[i]))][a[i]],dp[lst][j][k][l]+(a[i]!=l));
					dp[ni][j+1][k][l]=min(dp[ni][j+1][k][l],dp[lst][j][k][l]);
				}
			}
		}
	}
	for(int i=0;i<=m;i++)
	{
		for(int j=0;j<=p;j++)
		{
			for(int k=0;k<=8;k++)
			{
				ans=min(ans,dp[(n&1)][i][j][k]+pre[(j^p)&p]);
			}
		}
	}
	printf("Case %d: %d\n\n",now,ans);
}
int main()
{
	for(int i=0;i<(1<<8);i++)
	{
		for(int j=0;j<8;j++)
		{
			pre[i]+=((i>>j)&1);
		}
	}
	while(1)
	{
		now++;
		scanf("%d %d",&n,&m);
		if(n==0&&m==0)break;
		p=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			a[i]-=25;
			p=(p|(1<<a[i]));
		}
		solve();
	}
	return 0;
}

相關文章