POJ 2978 Colored stones(状压DP+逆向思维)

34 篇文章 1 订阅
20 篇文章 0 订阅
Colored stones
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 1887 Accepted: 882

Description

You are given a row of m stones each of which has one of k different colors. What is the minimum number of stones you must remove so that no two stones of one color are separated by a stone of a different color?

Input

The input test file will contain multiple test cases. Each input test case begins with a single line containing the integers m and k where 1 ≤m ≤ 100 and 1 ≤ k ≤ 5. The next line contains m integersx1, …, xm each of which takes on values from the set {1, …,k}, representing the k different stone colors. The end-of-file is marked by a test case withm = k = 0 and should not be processed.

Output

For each input case, the program should the minimum number of stones removed to satisfy the condition given in the problem.

Sample Input

10 3
2 1 2 2 1 1 3 1 3 3
0 0

Sample Output

2

Hint

In the above example, an optimal solution is achieved by removing the 2nd stone and the 7th stone, leaving three “2” stones, three “1” stones, and two “3” stones. Other solutions may be possible.

Source

Stanford Local 2005

题目大意:
    给出一个长度为N有K种颜色的石子的序列,求最小去除石子个数,使得全部颜色相同的石子在一起。

解题思路:
    正向思考的话,后效性非常强,删除石子的状态转移非常复杂。那么我们可以考虑逆向思考。我们用这些石子构造一个最长的所有颜色相同的石子在一起的序列。则这样的后效性就小了特别多,状态转移也就非常好写了。
    用dp[i][j][k]表示用前i个石头,最后一个为j,已经使用的石头的颜色的状态为k时,组成的最长序列。那么我们初始化另每一个石子创立一个只有它自己的序列。然后在枚举状态更新dp数组的时候对每个序列的各个情况不断更新。最后用N减去最长的序列即为所求。(具体状态转移见代码)。

AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define INF 0x3f3f3f3f

const int maxn=100+3;
const int maxk=5+1;
int color[maxn],N,K,dp[maxn][maxk][1<<maxk];//dp[i][j][k]表示用前i个石头,最后一个为j,已经使用的石头的颜色的状态为k时,组成的最长序列

int main()
{
    while(~scanf("%d%d",&N,&K)&&(N||K))
    {
        for(int i=0;i<N;++i)//初始化
            for(int j=0;j<K;++j)
                for(int k=0;k<(1<<K);++k)
                    dp[i][j][k]=0;
        for(int i=0;i<N;++i)
        {
            scanf("%d",&color[i]);
            --color[i];//颜色改为从1开始
            dp[i][color[i]][1<<color[i]]=1;
        }
        for(int i=0;i<N-1;++i)//使用刷表法更新dp数组
            for(int j=0;j<K;++j)
                for(int k=0;k<(1<<K);++k)
                    if(dp[i][j][k])//这个序列不为空,才进行添加
                    {
                        if(color[i+1]==j)//新加入的石子和最后一个相同,就直接加入
                            dp[i+1][j][k]=max(dp[i+1][j][k],dp[i][j][k]+1);
                        else if((1<<color[i+1])&k)//和最后一个颜色不同并且这个颜色以前出现过,则不加入
                            dp[i+1][j][k]=max(dp[i+1][j][k],dp[i][j][k]);
                        else
                        {
                            dp[i+1][j][k]=max(dp[i+1][j][k],dp[i][j][k]);//不加入
                            dp[i+1][color[i+1]][k|(1<<color[i+1])]=max(dp[i+1][color[i+1]][k|(1<<color[i+1])],dp[i][j][k]+1);//加入新颜色的石子
                        }
                    }
        int ans=INF;
        for(int j=0;j<K;++j)
            for(int k=0;k<(1<<K);++k)
                ans=min(ans,N-dp[N-1][j][k]);
        printf("%d\n",ans);
            
    }
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值