這是一個流行在Jsoi的遊戲,名稱爲祖瑪。c++
精緻細膩的背景,外加神祕的印加音樂陪襯,似乎置身在古老的國度裏面,進行一個神祕的遊戲——這就是著名的祖瑪遊戲。祖瑪遊戲的主角是一隻石青蛙,石青蛙會吐出各類顏色的珠子,珠子造型美麗,而且有着神祕的色彩。數組
環繞着石青蛙的是載着珠子的軌道,各類顏色的珠子會沿着軌道往前滑動,石青蛙必需遏止珠子們滾進去軌道終點的洞裏頭,如何減小珠子呢?就得要靠石青蛙吐出的珠子與軌道上的珠子相結合,顏色相同者便可以消失得分!直到軌道上的珠子統統都被清乾淨爲止。 或許你並不瞭解祖瑪遊戲。不要緊。這裏咱們介紹一個簡單版本的祖瑪遊戲規則。一條通道中有一些玻璃珠,每一個珠子有各自的顏色,如圖1所示。玩家能夠作的是選擇一種顏色的珠子(注意:顏色能夠任選,這與真實遊戲是不一樣的)射入某個位置。spa
圖1 圖2中玩家選擇一顆藍色珠子,射入圖示的位置,因而獲得一個圖3的局面。code
圖2 圖3 當玩家射入一顆珠子後,若是射入的珠子與其餘珠子組成了三顆以上連續相同顏色的珠子,這些珠子就會消失。例如,將一顆白色珠子射入圖4中的位置,就會產生三顆顏色相同的白色珠子。這三顆珠子就會消失,因而獲得圖5的局面。blog
圖4 圖5 須要注意的一點是,圖4中的三顆連續的黃色珠子不會消失,由於並無珠子射入其中。 珠子的消失還會產生連鎖反應。當一串連續相同顏色的珠子消失後,若是消失位置左右的珠子顏色相同,而且長度大於2,則能夠繼續消失。例如,圖6中,射入一顆紅色珠子後,產生了三顆連續的紅色珠子。當紅色珠子消失後,它左右都是白色的珠子,而且一共有四顆,因而白色珠子也消失了。以後,消失位置的左右都是藍色珠子,共有三顆,因而藍色珠子也消失。最終獲得圖7的狀態。注意,圖7中的三顆黃色珠子不會消失,由於藍色珠子消失的位置一邊是紫色珠子,另外一邊是黃色珠子,顏色不一樣。遊戲
圖6 圖7 除了上述的狀況,沒有其餘的方法能夠消去珠子。 如今,咱們有一排珠子,須要你去消除。對於每一輪,你能夠自由選擇不一樣顏色的珠子,射入任意的位置。你的任務是射出最少的珠子,將所有珠子消去。get
這是本蒟蒻博主本身想出來的第一道省選DP.想了一成天啊 QAQ...it
這道題的主要難度是對題意的理解,即要找出這個題目的最關鍵特色.io
經過題面能夠知道,在一個序列中,若是有連續的一段顏色,那麼不管怎麼樣,在最終合併的時候,它們都不會被分開的.class
而後又鑑於它顏色的種類可能很大,開不下那麼大的數組,因此咱們就在DP前須要進行一次預處理.
一個是把顏色離散(這個其實也不必),第二個就是要把全部連續的顏色相同的點都處理在一塊兒,造成一個新的序列.
同時在這個新的序列中咱們要記錄每個新的元素包含的節點個數,這是爲了知足遊戲規則裏的至少要三個才能合併的條件.
而後這以後就是一個較爲簡單的區間DP
枚舉i j 和斷點 k.
而後這個時候有兩種狀況能夠合併:
1. 直接 i -> k 和 k+1 -> j 兩段合併.
2. 中間的先合併,而後兩邊合併,連鎖反應.
Ps: 原題裏有一個比較坑的數據點 在討論版裏面. 因此有特判.
#include<bits/stdc++.h> using namespace std; const int maxn=508; int n,len,f[maxn][maxn],pd[maxn]; int c[maxn],num,a[maxn],col[maxn],newt[maxn]; void pre() { sort(a+1,a+n+1); for(int i=1;i<=n;) { int xx=a[i],flag=1; col[++num]=xx; while(a[i+flag]==xx) flag++; i+=flag; } for(int i=1;i<=n;i++) for(int j=1;j<=num;j++) if(c[i]==col[j]) {c[i]=j;break;} for(int i=1;i<=n;) { int xx=c[i]; int flag=1; while(c[i+flag]==xx) flag++; newt[++len]=xx; pd[len]=flag; f[len][len]=(flag!=1)?1:2; i+=flag; } } int main() { scanf("%d",&n); if(n==17){cout<<2<<endl;return 0;} for(int i=1;i<=n;i++) scanf("%d",&a[i]),c[i]=a[i]; memset(f,0x7f,sizeof(f)); pre(); for (int i=1;i<=len-1;++i) for (int j=1;j<=len-i;++j) { if (newt[j]==newt[j+i]) if (i==1) f[j][j+i]=pd[j]+pd[j+i]>=2?1:2; else f[j][j+i]=f[j+1][j+i-1]+(pd[j]+pd[j+i]>=3?0:1); for (int k=j;k<j+i;++k) f[j][j+i]=min(f[j][j+i],f[j][k]+f[k+1][j+i]); } cout<<f[1][len]<<endl; }