• 【算法•日更•第十五期】信息奥赛一本通1594:涂抹果酱题解


      废话不多说,直接上题:


    1594:涂抹果酱


    时间限制: 1000 ms         内存限制: 524288 KB
    提交数: 146     通过数: 46 

    【题目描述】

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

    现在 Sam 想知道:能令 Admin 满意的涂果酱方案有多少种。请输出方案数 mod 106 。若不存在满足条件的方案,请输出 0。

    【输入】

    输入共三行。

    第一行:N,M;

    第二行:K;

    第三行:M 个整数,表示第 K 行的方案。

    字母的详细含义见题目描述,其他参见样例。

    【输出】

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

    【输入样例】

    2 2 
    1 
    2 3

    【输出样例】

    3

    【提示】

    样例说明:

    方案一 方案二 方案三
    2 32 3 2 32 3 2 32 3
    1 21 2 3 13 1 3 23 2

    数据范围与提示:

    对于 30% 的数据,1N×M20

    对于 60% 的数据,1N1000,1M3

    对于 100% 的数据,1N10000,1M5

    【来源】


      这道题和上一次的题目十分相像,可以直接判定这是状态压缩动态规划。

      那么怎么状态压缩呢?这个果酱有三种颜色,所以我们就不能使用二进制了,而是采用三进制的方法状态压缩。

      题目中的果酱有红绿蓝三色,那么我们就分别让他们用0,1,2来表示。

      那么肯定要预处理,只要枚举好一行所有的状态即可,注意有些二进制的方法要变的,详见代码。

      先来说状态怎么设计,我们可以用f[i][j]来表示第i行状态为a[j],值为状态是否可行。总之先分析一下现在这张图是什么样的。

      

      其中第k行已经被涂过了,然后我们就可以分成这三个部分,分别是上图所示的三个部分(红绿蓝)。

      因为第k行已经被涂过了,所以可以把它看成是第0行。显然,这样蓝色部分和红色部分第i行的方案数是一样的(因为互相不受干扰)。

      

      忽略小编拙劣的画技。

      因此我们便利的行数只要是红色的行数就可以了,那么我们已经区分开了两个部分,怎样状态转移呢?

      显然,f[i][j]=sum{f[i-1][k]},(前提是不发生冲突)其中a[j]是当前行的状态,a[k]是上一行的状态,那么这一行的方案数自然就是上一行方案数的和呗。

      最后,我们要把所有状态的方案数都要加在一起,同时注意两个部分分别处理,方案总数是两个部分方案数的积。(乘法原理)

      好了,详见注释,代码如下:

     1 #include<iostream> 
     2 #define mod 1000000
     3 using namespace std;
     4 long long n,m,K,c[10000],cnt,a[10000],f[10001][200],s,num,correct[10000][10000],ans1,ans2;
     5 inline int check(int x)//判断行内冲突 
     6 {
     7     for(int i=m-1;i;i--) 
     8     if((x%c[i+1]/c[i])==(x%c[i]/c[i-1]))
     9     return 0;
    10     return 1;
    11 }
    12 inline int check2(int x,int y)//判断两行间的冲突 
    13 {
    14     for(int i=m-1;i>=0;i--) 
    15     if((x%c[i+1]/c[i])==(y%c[i+1]/c[i]))
    16     return 0;
    17     return 1;
    18 }
    19 int main()
    20 {
    21     cin>>n>>m>>K;c[0]=1;
    22     for(int i=1;i<=m;i++)
    23     {
    24         cin>>s;
    25         num*=3;
    26         num+=s-1;//三进制表示 
    27         c[i]=c[i-1]*3;//c是表示3进制数位的数组 
    28     }
    29     if(!check(num)){cout<<0;return 0;}//如果第K行有冲突,那么直接输出0 
    30     for(int i=0;i<c[m];i++)//逐个枚举一行的状态 
    31     {
    32         if(check(i)) a[++cnt]=i;//记录状态 
    33         if(i==num) f[0][cnt]=1;//初始化 
    34     }
    35     for(int i=1;i<=cnt;i++)//枚举当前行状态 
    36     for(int j=1;j<=cnt;j++)//枚举上一行状态 
    37     if(check2(a[i],a[j])) correct[i][j]=1;//判断是否发生冲突 
    38     int x=max(K-1,n-K);int y=min(K-1,n-K);//分成两个部分 
    39     for(int i=1;i<=x;i++)//枚举行 
    40     for(int j=1;j<=cnt;j++)//枚举当前行状态 
    41     for(int k=1;k<=cnt;k++)//枚举上一行状态 
    42     if(correct[j][k]) f[i][j]=(f[i][j]+f[i-1][k])%mod;//注意是否发生冲突 
    43     for(int i=1;i<=cnt;i++)//枚举状态数 
    44     {
    45         ans1+=f[x][i];
    46         ans1%=mod;
    47         ans2+=f[y][i];//两个部分分别处理 
    48         ans2%=mod;
    49     }
    50     cout<<ans1*ans2%mod;//乘法原理 
    51     return 0;
    52 }
  • 相关阅读:
    30个在线学习设计与开发的站点
    马云:你的一生到底该往哪个方向走?
    那些争议最大的编程观点
    Python 标识符
    Python 环境搭建
    Python 简介
    PyCharm 使用技巧
    Shell脚本———— /dev/null 2>&1详解
    linux 创建连接命令 ln -s 软链接
    scp命令详解
  • 原文地址:https://www.cnblogs.com/TFLS-gzr/p/11207835.html
Copyright © 2020-2023  润新知