B – 集合选数 (状态压缩)

  • 2019-02-07
  • 0
  • 0

题目链接:https://cn.vjudge.net/contest/281960#problem/B

思路:
首先构造一个根本想不出来的矩阵:
x 3x 9x…
2x 6x 18x…
4x 12x 36x…
. . .
. . .
题目中的要求可以转化成在矩阵中任选几个数要求这几个数不可以相邻,求方案数,1~n并没有出现在一个矩阵中,所以要构造多个矩阵,用乘法原理将每个矩阵的方案数相乘。
f[i][j]代表第i行状态为j时1~i行有多少个方案数

#include <iostream>
#include <bits/stdc++.h>

using namespace std;

#define mod 1000000001
#define inf 0x3f3f3f3f
#define ll long long
int bin[20];
int n;
ll ans=1;
int a[20][20],b[20],f[20][2048];
bool vis[100005];//数字有没有在前面的矩阵出现过

int cal(int x)
{
    memset(b,0,sizeof(b));
    a[1][1]=x;
    for(int i=2;i<=18;i++)//构造矩阵第一列
    {
        if(a[i-1][1]*2<=n)a[i][1]=a[i-1][1]*2;
        else a[i][1]=n+1;
    }
    for(int i=1;i<=18;i++)//构造矩阵
    {
        for(int j=2;j<=11;j++)
        {
            if(a[i][j-1]*3<=n)a[i][j]=a[i][j-1]*3;
            else a[i][j]=n+1;
        }
    }
    for(int i=1;i<=18;i++)
    {
        for(int j=1;j<=11;j++)
        {
            if(a[i][j]<=n)
            {
                b[i]+=bin[j-1];
                vis[a[i][j]]=1;
            }
        }
    }
    for(int i=0;i<=18;i++)
    {
        for(int x=0;x<=b[i];x++)
        {
            f[i][x]=0;
        }
    }
    f[0][0]=1;
    for(int i=0;i<18;i++)
    {
        for(int x=0;x<=b[i];x++)
        {
            if(f[i][x])//为0的话说明这一状态不成立,没必要向下传递了
            {
                for(int y=0;y<=b[i+1];y++)
                {
                    if(((x&y)==0)&&((y&(y>>1))==0))//x为第i行状态,y为i+1行状态,x&y判断上下两行有无相邻,y&(y>>1)判断i+1行左右有无相邻
                    {
                        f[i+1][y]=(f[i][x]+f[i+1][y])%mod;
                    }
                }
            }
        }
    }
    return f[18][0];
}

int main()
{
    bin[0]=1;
    for(int i=1;i<20;i++)
    {
        bin[i]=bin[i-1]*2;
    }
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            ans=(ans*cal(i))%mod;
        }
    }
    cout<<ans<<endl;
    return 0;
}

评论

还没有任何评论,你来说两句吧