【POJ1173】条形码

目录

一.题目

二.题解

三.代码

谢谢!


一.题目

二.题解

这道题有点懵呀。容斥原理根本想不到,那么就来想一下DP。

我们定义一个二维DP数组dp[i][j],表示宽度为i,共有j个条形的总情况数。

可以想到有两种情况:

1.这一位二维码与上一位二维码同色,连成一体:dp[i-1][j]

扫描二维码关注公众号,回复: 8762107 查看本文章

2.这一位二维码与上一位二维码异色,独成一条:dp[i-1][j-1]

但是还有一种特殊情况,就是如果与上一位同色,那么就是如果这一条二维码的长度超过了m,就要减去这种情况:dp[i-1-m][j]

所以状态转移方程就是:dp[i][j]=dp[i-1][j]+dp[i-1][j-1]-dp[i-m-1][j-1]


我们求到了总方案数,现在来求要求的那一个方案。

我们先预处理出输入的条形码存入数组,然后循环处理这个数组:

1.如果这一位是1,那么排在这个二维码之前的二维码,就是从这一位开始,前面连起来的1的长度比这个二维码的1连起来的长度的二维码种数,如:1011100就在1001100的后面

2.如果这一位是0,那么排在这个二维码之前的二维码,就是从这一位开始,后面连起来的0的长度比这个二维码的0连起来的长度的二维码种数,如:1011001就在1011000的后面


最后答案就出来了。

三.代码

/*
1.求和:
定义dp[i][j]表示宽度为i,共有个条形,每个条形长度不超m
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] - dp[i - m - 1][j - 1];独自占一条+与其他的混搭-每条长度超过m的不合法情况
2.算字典序
运用dp数组,可以发现从同一位置开始的两个条形,在前面的0越多越小,在前面的1越多越大
*/
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define M 40
#define LL long long
int n, k, m, s, len[M];
LL dp[M][M], ans;
char p[M];
void prepare (){//将条形码变为一个k位数的数,如:1001100->1222
    int tmp = 0;
    for (int i = 1; i <= k; i ++){
        if (i % 2 == 1)
            while (p[++ tmp] == '1')
                len[i] ++;
        else
            while (p[++ tmp] == '0')
                len[i] ++;
        tmp --;
    }
}
void DP (){//算字典序
    int tmp = n;
    for (int i = 1; i <= k; i ++){
        if (i % 2 == 1)
            for (int j = 1; j < len[i]; j ++)
                ans += dp[tmp - j][k - i];//这是1的情况,从同一位置开始,如果在前面的1越少,这个条形码的字典序越小
        else
            for (int j = m; j > len[i]; j --)
                if (tmp >= j)
                    ans += dp[tmp - j][k - i];//这是0的情况,从同一位置开始,如果在前面的0越多,这个条形码的字典序越小
        tmp -= len[i];
    }
}
int main (){
    scanf ("%d %d %d %d", &n, &k, &m, &s);
    dp[0][0] = 1;
    for (int i = 1; i <= n; i ++){//DP处理总和
        for (int j = 1; j <= k; j ++){
            dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
            if (i - 1 >= m)
                dp[i][j] -= dp[i - 1 - m][j - 1];
        }
    }
    printf ("%lld\n", dp[n][k]);
    for (int i = 1; i <= s; i ++){
        memset (len, 0, sizeof(len));
        ans = 0;
        scanf ("%s", p + 1);
        prepare ();
        DP ();
        printf ("%lld\n", ans);
    }
    return 0;
}
/*
样例:
7 4 3
5
1001110
1110110
1001100
1001110
1000100
*/
 

谢谢!

发布了61 篇原创文章 · 获赞 32 · 访问量 8370

猜你喜欢

转载自blog.csdn.net/weixin_43908980/article/details/88947337