zoukankan      html  css  js  c++  java
  • 条形码问题

    前言

    这题是前天,对,没错前天的模拟赛做到的。然而我今天才订正完。

    题面

    条形码是一种由亮条(Light Bar)和暗条(Dark Bar)交替出现且以暗条为起头的符号,每条都占有若干个单位宽。图33-1给出了一个含有4个条的条形码它延续了1+2+3+1=7单位的宽 。

    一般情况下BC(N,K,M)是一个包含所有由K个条总宽度正好为N个单位,每个条的宽度至多为M个单位性质的条形码组成的集合。

    例如:图33-1的条形码属于BC(7,4,3)而不属于BC(7,4,2)

    图33-2显示了集合BC(7,4,3)中的所有16个符号,其中1表示暗,0表示亮。

    图中所示条形码已按字典顺序排列冒号左边数字为条形码码的编号,图33-1的条形码在BC(7,4,3)书的编为4。

     输入

    输入的第一行为N、K、M的值(1≤N,K,M≤33)。

    第二行为数字S(0≤S≤100),而后的S行中,每行为一个图33-2那样描述的集合BC(N,K,M)中的一个条形码。

    输出

    你的程序应完成任务 

    1. 第一行是BC(N,K,M)中条形码的个数 
    2. 第二行起的S行中,每一行是输入文件对应条形码的编号;输入与输出数据中同一行相邻两个数之间用空格区分。

    分析

    § 1 枚举?

    理论上可行,但因为有每条宽上线M的限制,因此枚举时并不只是像生成全排列那样简单,需要注意的细节很多。

    § 2 换个思路

    我们考虑一下类似于Canter Expansion的思路,就是说把当前要求的条形码序列分解,然后求之前总的条形码个数,也就是当前的编号。(如何详细描述呢?我也不是很好表达,不明白的同学可以去看一下关于Canter Expansion的文章)

    § 3 动态规划

    没错,我们用DP来求那个“之前总的条形码个数”。

    DP[i][j][k][x]表示由j个条总宽度正好为i个单位,首条的颜色为x,宽度为k个单位的条形码的编号。

    则有DP[i][j][k][x] = ∑DP[i - 1][j][k - 1][x] + ∑DP[i - 1][j - 1][l][!x]

    其中1 ≤ l ≤ M, !x表示与x相反的颜色。

    参考代码

    要点均注在注释中

    #include <cstdio>
    const int MAXN = 35;
    
    int N, K, M, S, DP[MAXN][MAXN][MAXN][2];
    char str[MAXN];
    
    void init();
    
    int main() {
        scanf("%d%d%d%d", &N, &K, &M, &S);
        init();
        int i, ans = 0, cnt, len, j, k, tmp1, tmp2;
        for (i = 1; i <= M; i++) ans += DP[N][K][i][1];  // 总的条形码个数
        printf("%d
    ", ans);
        for (i = 0; i < S; i++) {
            scanf("%s", str);
            cnt = len = 1; ans = 0;  // cnt记录当前统计到的条码数,len记录当前条宽度
            for (j = 1; j < N; j++) {
                if (str[j] == '1') {
                    if (str[j - 1] == '1')
                        for (k = 1, tmp1 = N - j, tmp2 = K - cnt; k <= M; k++) ans += DP[tmp1][tmp2][k][0];  // 如果是暗条,那么累加1~M长度的亮条
                    else for (k = M - len, tmp1 = N - j, tmp2 = K - cnt + 1; k > 0; k--) ans += DP[tmp1][tmp2][k][0];  // 如果是亮条,那么要减去这条亮条长度
                }
                if (str[j] == str[j - 1]) len++;
                else cnt++, len = 1;
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
    
    void init() {  // DP数组初始化,由前推后,这样实现比较简单
        DP[1][1][1][1] = DP[1][1][1][0] = 1;
        int i, j, k;
        for (i = 1; i < N; i++) 
            for (j = 1; j <= K; j++)
                for (k = 1; k <= M; k++) {
                    if (k < M) DP[i + 1][j][k + 1][0] += DP[i][j][k][0];
                    DP[i + 1][j + 1][1][1] += DP[i][j][k][0];
                    if (k < M) DP[i + 1][j][k + 1][1] += DP[i][j][k][1];
                    DP[i + 1][j + 1][1][0] += DP[i][j][k][1];
                }
    }

    总结

    这题的满分思路比较难考虑到,说实话暴力都很难写,我也是看了dalao的AC代码才明白的。

    重点呢在于你把编号转换成方案数,再导出这个转移方程的过程。

    当然这篇博客不得不说我写得有些含糊,当然我自己明白,可是我是真的表达不出来。

  • 相关阅读:
    PHP flush()与ob_flush()的区别
    IE 浏览器各个版本 JavaScript 支持情况一览表
    Jquery元素选取、常用方法
    JS阻止事件冒泡
    Ajax传递路径问题及解决
    JS时间戳格式化日期时间
    UEditor编辑器的使用
    使用PHPMailer发送邮件
    服务器数据库编码格式问题
    三级联动
  • 原文地址:https://www.cnblogs.com/CaptainSlow/p/9262097.html
Copyright © 2011-2022 走看看