P1777 幫助_NOI導刊2010提高(03)

也許更好的閱讀體驗

Description\mathcal{Description}

Bubu的書架亂成一團了!幫他一下吧!

他的書架上一共有n本書。我們定義混亂值是連續相同高度書本的段數。例如,如果書的高度是30,30,31,31,32,那麼混亂值爲3,30,32,32,31的混亂度也是3,但31,32,31,32,31的混亂度是5,這實在是太亂了。

Bubu想盡可能地減少混亂度,但他有點累了,所以他決定最多取出k本書,再隨意將它們放到書架上。你能幫助他嗎?

Solution\mathcal{Solution}

考慮取出一本書把其認爲是刪掉這本書,因爲總有辦法使其不會影響到後面的取書過程
特殊的是如果將一種書全部刪掉,那麼應該還是會有1的混亂度
如果考慮dpdp方式爲如何刪書,那麼這個過程實在複雜
如果考慮最後剩下的書,那麼複雜度是2n2^n妥妥的TT
但是仔細的想一下,會發現,考慮最後剩下的書時,並不是前面所有的狀態都對現在的狀態會有影響
即如果一本書隔得較遠,則與你當前這本書沒有多大關係,在保證所有的狀態會被考慮在內的情況下,考慮狀態壓縮
f[i][j][s][l]f[i][j][s][l]表示前ii本書,刪掉了jj本,剩下書的種類的集合爲ss,上一本沒有被刪的書的種類是ll

考慮第ii本書

  • 若刪掉這本書,則有f[i][j+1][s][l]=min(f[i][j][s][l],]f[i1][j][s][l])f[i][j+1][s][l]=min\left(f[i][j][s][l],]f[i-1][j][s][l]\right)
  • 若保留這本書,則有f[i][j][s(1<<h[i])][h[i]]=min(f[i][j][s(1<<h[i])][h[i]],f[i1][j][s][l]+(h[i]==l?0:1))f[i][j][s|(1<<h[i])][h[i]]=min\left(f[i][j][s|(1<<h[i])][h[i]],f[i-1][j][s][l]+\left(h[i]==l?0:1\right)\right)

最後的時候回來考慮剩下的書的種類集合,如果原本所有書的種類集合SS有一種書,現在沒有了,那麼我們就多減了一個1,加回來就是

Code\mathcal{Code}

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年10月04日 星期五 08時36分03秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cstring>
#define inf 0x7f7f7f7f
using namespace std;
const int maxn = 111;
const int lim = 256;
//{{{cin
struct IO{
	template<typename T>
	IO & operator>>(T&res){
		res=0;
		bool flag=false;
		char ch;
		while((ch=getchar())>'9'||ch<'0')	flag|=ch=='-';
		while(ch>='0'&&ch<='9')	res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
		if (flag)	res=~res+1;
		return *this;
	}
}cin;
//}}}
int n,k,cas;
int h[maxn],one[lim+5];
int f[2][maxn][lim+5][11];
bool now,pre;
//f[i][j][k][l] 前i本書選了j本書,存在的書的種類集合爲k,上一本書爲l
int main()
{
	for (int i=0;i<lim;++i){
		for (int j=0;j<8;++j)
			if (i&(1<<j))	++one[i];
	}
	while (true){
		cin>>n>>k;
		if (n+k==0)	break;
		int S=0,mh=0;
		for (int i=1;i<=n;++i){
			cin>>h[i];
			h[i]-=25;
			S|=1<<h[i];
			mh=max(mh,h[i]);
		}
		++mh;
		memset(f[0],0x7f,sizeof(f[0]));
		now=0;
		f[0][0][1<<h[1]][h[1]]=1;
		f[0][1][0][mh]=0;
		for (int i=2;i<=n;++i){
			now^=1,pre=!now;
			memset(f[now],0x7f,sizeof(f[now]));
			for (int j=0;j<=k;++j){
				for (int s=0;s<=S;++s)
					for (int l=0;l<=mh;++l)
						if (f[pre][j][s][l]!=inf){
							f[now][j][s|(1<<h[i])][h[i]]=min(f[now][j][s|(1<<h[i])][h[i]],f[pre][j][s][l]+(h[i]==l?0:1));
							f[now][j+1][s][l]=min(f[now][j+1][s][l],f[pre][j][s][l]);
						}
			}
		}
		int ans=inf;
		for (int j=0;j<=k;++j)
			for (int s=0;s<=S;++s)
				for (int l=0;l<mh;++l)
					if (f[now][j][s][l]!=inf){
						int take=S^s;
						ans=min(ans,f[now][j][s][l]+one[take]);
					}
		printf("Case %d: %d\n\n",++cas,ans);
	}
	return 0;
}

如有哪裏講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章