[poj 2978]Colored Stones[状态压缩DP]

题意:

给出n个石子,一共m种颜色.问最少去掉几个石子使得同种颜色全连续.

思路见注释.

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

const int kMAX=105;

/// dp[x][y][z],x指的是[到达第x个石子,包含(意思是参与讨论,并不是说一定留下)第x个石子]的情况下,颜色组合为y(每种颜色占一位),
/// 最后一颗石子的颜色为z的最多剩余石子数,因为[第x颗石子去留不一定],所以z不一定等于x的颜色
int dp[kMAX][1<<6][6];

int main()
{
    int n,m,tmp;

    while(scanf("%d%d",&n,&m)==2 && (n+m))
    {
        memset(dp,0,sizeof(dp));
        int len=(1<<m);//状态的总数
        for(int i=1;i<=n;++i)//一颗一颗拿石子
        {///拿起一颗石子,要进行两重循环:遍历所有的颜色组合->遍历所有的结尾种类
         ///i是递增的,而后两维则根据选择的情况来确定
            scanf("%d",&tmp);
            --tmp;//编号修正,可直接对应位置
            for(int j=0;j<len;++j)
            {
                for(int k=0;k<m;++k)///对于每种颜色k结尾的情况
                {
                    if(!(j&(1<<tmp))) /// 如果第i颗石子的颜色[不存在于当前研究的状态j中]
                    {
                        dp[i][ j|(1<<tmp) ][tmp] = max(dp[i][ j|(1<<tmp) ][tmp], dp[i-1][j][k]+1);///使用这颗石子
                        ///使用这颗石子时,所对应的dp下标随之转移.x坐标跳转到更靠下的位置(也就是第二维)
                        ///当只改变k时,上式左边对应的位置是不变的.对于不同的k,选择出一种方案使得左值最大(max使得其达到最大时可以保持住)

                        /**昨天的数位dp预处理(HDU3555Bomb)都是确定的转移关系,而对于"选择最佳方案"类的dp问题,一般是取最值的(如背包)**/

                        ///两重循环,只是保证了每种情况都会check一遍,并不是说在当前循环中就一定填哪个位置.
                        ///从这个层面上说,体现了Dynamic.这也解释了为什么需要取max:因为这个位置可能之前已经填过了,
                        ///也就是通过之前的某种路线已经到达过这个状态,取max就是动态的选取最优的路线.数值已经刻画了选择的历史.

                        dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k]);///不用这颗石子
                        /// 不用这颗石子时,因为一样是讨论到了i,所以同样要更新

                        ///总的来说,每次更新都不能直接覆盖原来的值,而要去max,以免覆盖了之前已经达到的可行的方案.而且总不会出错.
                    }
                    else    /// 如果第i颗石子的颜色[存在于当前研究的状态j中]
                    {
                            if(k == tmp)
                                dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k]+1);
                                /// 如果第i颗石子的颜色和在i前面剩余石子中的最后一颗石子颜色一样,则i必定留下来
                            else
                                dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k]); /// 否则就不留
                    }
                }
            }
        }
        int ans=0;
        for(int k=0;k<m;++k)
            for(int j=0;j<len;++j)
                ans=max(ans,dp[n][j][k]);
        printf("%d\n",n-ans);
    }

    return 0;
}

自己敲一遍~

#include <cstdio>
#include <cstring>
using namespace std;
int max(int a, int b)
{
    int diff = b - a;
    return b - (diff & (diff >> 31));
}
const int MAXN = 105;
int dp[MAXN][1<<6][6];
/** 324K 0MS
        dp[i][j][k]:
            dealing with the i-th stone
            with status j
            end up with color k;
**/
int main()
{
    int n,m;
    while(scanf("%d %d",&n,&m)==2 && (n+m))
    {
        memset(dp,0,sizeof(dp));
        int tmp;
        int len = 1<<m;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&tmp);
            tmp--;
            for(int j=0;j<len;j++)
            {
                for(int k=0;k<m;k++)
                {
                    if(!(j & (1<<tmp)))
                    {
                        dp[i][j|(1<<tmp)][tmp] = max(dp[i][j|(1<<tmp)][tmp],dp[i-1][j][k]+1);
                        dp[i][j][k] = max(dp[i][j][k],dp[i-1][j][k]);
                    }
                    else
                    {
                        if(k==tmp)
                            dp[i][j][tmp] = max(dp[i][j][tmp],dp[i-1][j][tmp]+1);
                        else
                            dp[i][j][k] = max(dp[i][j][k],dp[i-1][j][k]);
                    }
                }
            }
        }
        int ans = 0;
        for(int j=0;j<len;j++)
            for(int k=0;k<len;k++)
                ans = max(ans,dp[n][j][k]);
        printf("%d\n",n - ans);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值