/*题目描述:-----------------------------------------------------------------------------------------------*/
把含有k种不同颜色的m个石子放成一条线上。现在要问你怎么才能取走
最少的石子,使得没有两个相同颜色的石子之间含有其它的颜色
输入:
有多组测试数据,每组测试数据有两行,第一行是m和k,1 <=m <=1000,
1 <=k <=5,第二行就是那m个石子的颜色,用1到k表示,最后输入0 0表示
程序结束。
输出:
对于每组测试数据,请你找出最少取走的石子数目并输出
样例输入:
10 3
2 1 2 2 1 1 3 1 3 3
0 0
样例输出:
2
/*算法描述与分析 ----------------------------------------------------------------------------------------*/
思路:动态规划法。
假设前n个石子的取走方式已知,思考对于第n+1个石子的取法。
首先,假设第n个石子与第n+1个相同,则n+1个石子的取法与n个石子的取法相同。
其次,若第n个石子与第n+1个不同,则对于n+1个石子有两种取的方法。
其一、取走第n+1个石子,则取的数目=n个石子的取的数据 + 1;
其二、前n个石子的取法中,查找这样的取法:“取完后没有与第n+1石子颜色相同的石子”。去所有这些取法中,取走数目最小的一个。
接下来,说明动态规划中所使用的状态。数组dp[1000][32][5]。dp[n][i][j]中,n表示加入第n个石子后。
i的二进制数有5位,第x位表示第x种,当前取法中,取完后,是否包含第x种颜色。1表示包含,0表示不包含。
j表示取法取完后,最后一种颜色是j。例如:对于样例中的数据,
输入2后,dp[0][2][1]=0;
输入1后,dp[1][2][1]=1; dp[1][1][0]=1; dp[1][3][0]=0;
输入2后,dp[2][2][1]=1; dp[2][1][0]=2; dp[2][3][0]=1; dp[2][3][1]=1;
dp[n][i][j]=min(dp[n-1][i-(1<<j)][k]), min(dp[n-1][i][k]+1));(0<=k<=4)
实际算法中,可以省略n,用dp[i][j]。
逐一加入石子,计算dp[i][j]。加入所有石子后,打印min(dp[i][j]);
算法时间复杂度O(m*(2^k)*k)
/*源代码 及测试例子------------------------------------------------------------------------------------*/
static const int poo=~(1<<31);
static int bound=5;
#define min(x,y) ((x>y) ? x=y : x)
class ColorStones{
public:
void doit();
};
void ColorStones::doit(){
int stonenums, colors, i,j,k;
int tin, ans;
int dp[1<<bound][bound];
while(cin>>stonenums>>colors){
if(!colors) break;
memset(dp, poo, sizeof(dp));
for(i = 0; i < stonenums; i++){
cin>>tin;tin--;
for(j=0;j<bound;j++){
if(j==tin) continue;
for(k=(1<<bound)-1;k>0;k--){
if(!(k&(1<<tin))) {
min(dp[k|(1<<tin)][tin],dp[k][j]);
}
if(dp[k][j]!=poo) dp[k][j]++;
}
}
if(dp[1<<tin][tin]==poo) dp[1<<tin][tin]=i;
}//end for
ans=poo;
for(j=(1<<bound)-1;j>0;j--)
for(k=0;k<bound;k++){
min(ans,dp[j][k]);
}
cout<<ans<<endl;
}//end while
return;
}