码迷,mamicode.com
首页 > 其他好文 > 详细

涂抹果酱(状压dp)

时间:2019-08-04 01:45:31      阅读:176      评论:0      收藏:0      [点我收藏+]

标签:main   alt   指正   space   als   cdc   oid   span   psu   

涂抹果酱

题目描述

Tyvj两周年庆典要到了,Sam想为Tyvj做一个大蛋糕。蛋糕俯视图是一个N×M的矩形,它被划分成N×M个边长为1×1的小正方形区域(可以把蛋糕当成N行M列的矩阵)。蛋糕很快做好了,但光秃秃的蛋糕肯定不好看!所以,Sam要在蛋糕的上表面涂抹果酱。果酱有三种,分别是红果酱、绿果酱、蓝果酱,三种果酱的编号分别为1,2,3。为了保证蛋糕的视觉效果,Admin下达了死命令:相邻的区域严禁使用同种果酱。但Sam在接到这条命令之前,已经涂好了蛋糕第K行的果酱,且无法修改。
现在Sam想知道:能令Admin满意的涂果酱方案有多少种。请输出方案数?mod10^6。若不存在满足条件的方案,请输出0。

输入描述:

输入共三行。
第一行:N,M;
第二行:K;
第三行:M个整数,表示第K行的方案。
字母的详细含义见题目描述,其他参见样例。

输出描述:

输出仅一行,为可行的方案总数。
示例1

输入

2 2 
1 
2 3

输出

3

说明

技术图片

备注:

对于30%的数据,1≤N×M≤20;
对于60%的数据,1≤N≤1000,1≤M≤3;
对于100%的数据,1≤N≤10000,1≤M≤5。

题目思路:
因为有三种果酱,所以考虑三进制状压dp,首先初始化三进制的合法状态(相邻的不能一样),用一个vector保存
f[i][j]表示第i行状态为[v[j]]时前i行的方案数,judge用来判断上下两行是否合法,index代表第k行状态在vector中的下标。
因为第k行已经固定,所以要分为两个阶段,第一阶段为k行以上的方案数,第二阶段为k行以下的方案数,注意第一阶段结束后f[k][index]要重新归1。
最后的答案就是两个阶段相乘的方案数取模

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define mod 1000000
using namespace std;
ll n,m,k,a[6],f[10010][100],mode,ans1,ans2;
vector<ll> v;
void init(int x,int y)//初始化合法状态 
{
    if(x==m)
    {
        v.push_back(y);
        return ;
    }
    else if(x==0)
    {
        init(1,0),init(1,1),init(1,2);
        return ;
    }
    if((int)(y/pow(3,x-1))%3==0)
        init(x+1,y+1*pow(3,x)),init(x+1,y+2*pow(3,x));
    else if((int)(y/pow(3,x-1))%3==1)
        init(x+1,y+0*pow(3,x)),init(x+1,y+2*pow(3,x));
    else
        init(x+1,y+0*pow(3,x)),init(x+1,y+1*pow(3,x));
}
bool judge(int x,int y)//判断x状态和y状态分别为上下两行时是否合法 
{
    int wei = m;
    while(wei--)
    {
        int a = x%3,b = y%3;
        if(a==b)
            return false;
        x/=3,y/=3;
    }
    return true;
}
int main()
{
    cin>>n>>m>>k;
    for(int i = 0;i<m;i++)
    {
        cin>>a[i];
        mode+=pow(3,m-1-i)*(a[i]-1);//mode记录第k行状态 
    }  
    init(0,0);
    int index = find(v.begin(),v.end(),mode)-v.begin();
    if(index==v.size())//如果第k行的状态不合法 
    {
        cout<<0;
        return 0;
    }  
    if(k==1)//如果k为1,那么第一阶段方案数为1 
    {
        ans1 = 1;
        f[k][index] = 1;
    } 
    if(k!=1)
    {
        for(int i = 0;i<v.size();i++)
            f[1][i] = 1;
    } 
    for(int i = 2;i<=n;i++)
    {
        if(i==k)
        {
            for(int l = 0;l<v.size();l++)
            {
                if(judge(mode,v[l]))
                {
                    f[i][index]+=f[i-1][l];
                    f[i][index]%=mod;
                }
            }
            ans1 = f[i][index]%mod;
            f[i][index] = 1;
            continue;
        }
        for(int j = 0;j<v.size();j++)
        {
            for(int l = 0;l<v.size();l++)
            {
                if(judge(v[j],v[l])&&f[i-1][l])
                {
                    f[i][j]+=f[i-1][l];
                    f[i][j]%=mod;
                }
            }
        }
    }
    for(int j = 0;j<v.size();j++)
        ans2 = (ans2+f[n][j])%mod;
    cout<<(ans1*ans2)%mod;
    return 0;
}

 

如果有错误的地方,还请各位大佬指正。

涂抹果酱(状压dp)

标签:main   alt   指正   space   als   cdc   oid   span   psu   

原文地址:https://www.cnblogs.com/loganacmer/p/11296808.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!