状压dp(一)——#10172. 「一本通 5.4 练习 1」涂抹果酱

题目链接:https://loj.ac/problem/10172
解题思路
就和国王问题一样,只不过国王是二进制,这题需要转化为三进制,然后从k往前dp,再从k往后dp,两个方案数相乘即是答案。
AC代码

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod=1e6;
const int maxn=1e4+5;
ll dp[maxn][250];
int mp[6];
int n,m,k,cnt;
int sta;
int st[250];
bool check(int a)
{
    int tmp=-1,last=-1;
    for(int i=1;i<=m;++i)
    {
        tmp=a%3;
        if(tmp==last)
        return false;
        last=tmp;
        a/=3;
    }
    return true;
}
int quickpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1)
        res=res*a;
        b>>=1;
        a=a*a;
    }
    return res;
}
bool judge(int a,int b)
{
    for(int i=1;i<=m;++i)
    {
        if(a%3==b%3)
        return false;
        a/=3;
        b/=3;
    }
    return true;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;++i)
    {
        scanf("%d",&mp[i]);
        sta=sta*3+mp[i]-1;
    }
    int maxx=quickpow(3,m);
    for(int i=0;i<maxx;++i)
    {
        if(check(i))
        st[++cnt]=i;
    }
    int pos=-1;
    for(int i=1;i<=cnt;++i)
    {
        if(st[i]==sta)
        {
            pos=i;
            break;
        }
    }
    if(pos==-1)
    {
        puts("0");
        return 0;
    }
    dp[k][pos]=1;
    for(int i=k-1;i>=1;i--)
    for(int j=1;j<=cnt;++j)
    for(int k=1;k<=cnt;++k)
    {
        if(judge(st[j],st[k]))
        dp[i][j]=(dp[i][j]+dp[i+1][k])%mod;
    }
    for(int i=k+1;i<=n;++i)
    for(int j=1;j<=cnt;++j)
    for(int k=1;k<=cnt;++k)
    {
        if(judge(st[j],st[k]))
        dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
    }
    ll ans1=0,ans2=0;
    for(int i=1;i<=cnt;++i)
    ans1=(ans1+dp[1][i])%mod;
    for(int i=1;i<=cnt;++i)
    ans2=(ans2+dp[n][i])%mod;
    if(k==1)
    printf("%lld\n",ans2);
    else if(k==n)
    printf("%lld\n",ans1);
    else
    printf("%lld\n",ans1*ans2%mod);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44491423/article/details/108277218