[洛谷]P2145 [JSOI2007]祖碼 (#區間dp)

題目描述

這是一個流行在Jsoi的遊戲,名稱爲祖瑪。

精緻細膩的背景,外加神祕的印加音樂襯托,彷佛置身在古老的國度裏面,進行一個神祕的遊戲——這就是著名的祖瑪遊戲。祖瑪遊戲的主角是一隻石青蛙,石青蛙會吐出各種顏色的珠子,珠子造型美麗,並且有着神祕的色彩。

環繞着石青蛙的是載着珠子的軌道,各種顏色的珠子會沿着軌道往前滑動,石青蛙必需遏止珠子們滾進去軌道終點的洞裏頭,如何減少珠子呢?就得要靠石青蛙吐出的珠子與軌道上的珠子相結合,顏色相同者即可以消失得分!直到軌道上的珠子通通都被清乾淨爲止。 或許你並不瞭解祖瑪遊戲。沒關係。這裏我們介紹一個簡單版本的祖瑪遊戲規則。一條通道中有一些玻璃珠,每個珠子有各自的顏色,如圖1所示。玩家可以做的是選擇一種顏色的珠子(注意:顏色可以任選,這與真實遊戲是不同的)射入某個位置。

圖1 圖2中玩家選擇一顆藍色珠子,射入圖示的位置,於是得到一個圖3的局面。

圖2 圖3 當玩家射入一顆珠子後,如果射入的珠子與其他珠子組成了三顆以上連續相同顏色的珠子,這些珠子就會消失。例如,將一顆白色珠子射入圖4中的位置,就會產生三顆顏色相同的白色珠子。這三顆珠子就會消失,於是得到圖5的局面。

圖4 圖5 需要注意的一點是,圖4中的三顆連續的黃色珠子不會消失,因爲並沒有珠子射入其中。 珠子的消失還會產生連鎖反應。當一串連續相同顏色的珠子消失後,如果消失位置左右的珠子顏色相同,並且長度大於2,則可以繼續消失。例如,圖6中,射入一顆紅色珠子後,產生了三顆連續的紅色珠子。當紅色珠子消失後,它左右都是白色的珠子,並且一共有四顆,於是白色珠子也消失了。之後,消失位置的左右都是藍色珠子,共有三顆,於是藍色珠子也消失。最終得到圖7的狀態。注意,圖7中的三顆黃色珠子不會消失,因爲藍色珠子消失的位置一邊是紫色珠子,另一邊是黃色珠子,顏色不同。

圖6 圖7 除了上述的情況,沒有其他的方法可以消去珠子。 現在,我們有一排珠子,需要你去消除。對於每一輪,你可以自由選擇不同顏色的珠子,射入任意的位置。你的任務是射出最少的珠子,將全部珠子消去。

輸入格式

第一行一個整數n(n ≤ 500),表示珠子的個數 第二行n個整數(32位整數範圍內),用空格分割,每個整數表示一種顏色的珠子。

輸出格式

一個整數,表示最少需要射出的珠子個數。

輸入輸出樣例

輸入 #1複製

9
1 1 2 2 3 3 2 1 1

輸出 #1複製

1

思路

看到這種合併類問題能立刻想到區間dp。但是似乎拿到手沒有任何頭緒......

我們可以先把顏色相同的放在一起處理。如下代碼:

cin>>a[++c];//第一個元素 
for(i=2;i<=n;i++)//合併顏色相同的球 
{
	int x;
	cin>>x;//下一個球 
	if(x==a[c]) cnt++;//如果顏色一樣,cnt++,表示顏色相同的球的數量+1,cnt一開始初始化爲1 
	else
	{
		list[c++]=cnt;//這種顏色的球有cnt個 
		cnt=1;//重置 
		a[c]=x;//將這個球的顏色放入原數組 
	}
}

然後我們就可以令dp[i][j]表示在區間[i,j]中最少需要插入的球的個數。初始化也很容易想到:如果一個地方有2個或以上需要插入1個,否則就要插入2個。

for(i=1;i<=c;i++)
{
	if(list[i]>=2) dp[i][i]=1;
	else dp[i][i]=2;
}

如果a[i]=a[j]說明區間兩端顏色相同,其中保證中間的部分已經合併完畢,所以我們就可以這樣推:

如果list[i]+list[j]>2,即超過2個,則:

dp[i][j]=dp[i+1][j-1]

如果list[i]+list[j]=2,則有2個,絕不可能出現1個,則:

dp[i][j]=dp[i+1][j-1]+1

即增加一個小球。

如果a[i]≠a[j]就正常跑一遍區間dp即可,推k,然後從[i,k]和[k+1,j]合併完後取最優解。

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]

#include <stdio.h>
#include <iostream>
#include <memory.h>
#define N 2001
#define inf 2e9+7
using namespace std;
int dp[N][N],n,a[N],list[N],s,cnt(1),c;
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	register int i,j,k,l;
	cin>>n;
	memset(dp,inf,sizeof(dp));
	cin>>a[++c];//第一個元素 
	for(i=2;i<=n;i++)//合併顏色相同的球 
	{
		int x;
		cin>>x;//下一個球 
		if(x==a[c]) cnt++;//如果顏色一樣,cnt++,表示顏色相同的球的數量+1,cnt一開始初始化爲1 
		else
		{
			list[c++]=cnt;//這種顏色的球有cnt個 
			cnt=1;//重置 
			a[c]=x;//將這個球的顏色放入原數組 
		}
	}
	list[c]=cnt;
	for(i=1;i<=c;i++)
	{
		if(list[i]>=2) dp[i][i]=1;
		else dp[i][i]=2;
	}
	for(l=2;l<=c;l++)
	{
		for(i=1;i<=c;i++)
		{
			j=l+i-1;
			if(a[i]==a[j])
			{
				if(list[i]+list[j]>2)
				{
					dp[i][j]=dp[i+1][j-1];
				}
				else
				{
					dp[i][j]=dp[i+1][j-1]+1;
				}
			}
			for(k=i;k<=j;k++)
			{
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
			}
		}
	}
	if(dp[1][c]==3)//特判,不然最後一個點WA 
	{
		cout<<2<<endl;
		return 0;
	}
	cout<<dp[1][c]<<endl;
	return 0;
}

 

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