题目

条形码是一种由亮条(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。

输入格式:

   输入文件Input4.DAT的第一行为N、K、M的值(1≤N,K,M≤33)。第二行为数字S(0≤S≤100),而后的S行中,每行为一个图33-2那样描述的集合BC(N,K,M)中的一个条形码。

输出格式:

你的程序应完成任务:
A、把输出内容写入文件OUPUT4.DAT。第一行是BC(N,K,M)中条形码的个数。
B、OUPUT.DAT的第二行起的S行中,每一行是输入文件对应条形码的编号;输入与输出数据中同一行相邻两个数之间用空格区分。

 
首先是求出条形码个数。可以很容易地得出状态转移方程:

ans(n,k,m)=sum{ans(n-x,k-1,m)}(1<=x<=min(m,n-k+1),km>=n)(n>k)
ans(k,k,m)=1
ans(t,k,m)=0(t<k)

然后是求每个条形码的序号,这个需要一些技巧。

***记一下

举例:1101000,求序号
l[1]=2,l[2]=1,l[3]=1,l[4]=3
比其小的有:
先是l[1]<2的(ans[7-1][3]=7)
再是l[1]=2,l[2]>1的(ans[7-2-2][2]+ans[7-2-3][2]=2+1=3)
还有l[1]=2,l[2]=1,l[3]<1的(0)
还有l[1]=2,l[2]=1,l[3]=1,l[4]>3的(0)
因此其序号是7+3+0+0-1+1=10
其他的以此类推

***

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int ans[][];
int n,m,k,num,temp,temp2;
int l[];
int s;
string s1;
int ans2;
int dfs(int n,int k)
{
if(k*m<n)
{
ans[n][k]=;
return ;
}
if(n>k)
{
ans[n][k]=;
for(int i=;i<=min(m,n-k+);i++)
if(ans[n-i][k-]==-)
ans[n][k]+=dfs(n-i,k-);
else
ans[n][k]+=ans[n-i][k-];
}
else if(n==k)
ans[n][k]=;
else ans[n][k]=;
return ans[n][k];
}
int main()
{
int i,p,j,j1;
memset(ans,-,sizeof(ans));
scanf("%d%d%d",&n,&k,&m);
printf("%d\n",dfs(n,k));
scanf("%d",&s);
for(i=;i<=s;i++)
{
ans2=;
cin>>s1;
p=;
num=;
for(j=;j<s1.length()-;j++)
{
p++;
if(s1[j]!=s1[j+])
{
l[++num]=p;
p=;
}
}
l[++num]=p+;
temp=n;
temp2=k;
for(j=;j<=num;j++)
{
temp2--;
if(j%==)
for(j1=;j1<l[j];j1++)
{
ans2+=ans[temp-j1][temp2];
}
else
for(j1=l[j]+;temp-j1>=temp2&&j1<=m;j1++)
{
ans2+=ans[temp-j1][temp2];
}
temp-=l[j];
}
printf("%d\n",ans2);
//1101110
//
}
return ;
}

再贴一段其他人的代码

#include<cstdio>
#include<algorithm>
using namespace std;
int n,k,m,s,a[];
long long f[][];
char c[];
int main(){
scanf("%d%d%d%d",&n,&k,&m,&s);
f[][]=;
for(int i=;i<=n;i++)
for(int j=;j<=k;j++)
for(int l=;l<=min(m,i);l++)
f[i][j]=f[i][j]+f[i-l][j-];
printf("%d\n",f[n][k]);
for(int i=;i<=s;i++){
scanf("%s",c);c[n]=(c[n-]-'')^+'';
int ans=,sum1=,sum2=,num=,p=;
for(int j=;j<=n;j++)if(c[j]!=c[j-])a[++num]=p,p=;else p++;
for(int j=;j<=num;j++){
sum2++;
if(j&)for(int l=a[j]-;l>=;l--)ans+=f[n-sum1-l][k-sum2];
else for(int l=a[j]+;l<=m;l++)ans+=f[n-sum1-l][k-sum2];
sum1+=a[j];
}
printf("%d\n",ans);
}
}

再贴一段讲解,并没有什么卵用:

动态规划和搜索有着千丝万缕的关系,我们先来看一个例子:

一、问题描述

“条形码”是一种由亮条(light bar)和暗条(dark bar)交替出现,且以暗条起头的符号。每个“条”(bar)都是若干个单位宽。图1给出了一个含4个“条”的“条形码”,它延续了1+2+3+1=7个单位宽。

一般情况下,BC(n,k,m)是一个包含所有由:k个“条”,总宽度正好为n个单位,每个“条”的宽度至多为m个单位的性质的“条形码”组成的集合。例如:图1的条形码属于BC(7,4,3),而不属于BC(7,4,2)。

0: 1000100 | 8: 1100100

1: 1000110 | 9: 1100110

2: 1001000 | 10: 1101000

3: 1001100

| 11: 1101100

4: 1001110 | 12: 1101110

5: 1011000 | 13: 1110010

6: 1011100 | 14: 1110100

7: 1100010 | 15: 1110110

图1 图2

图2显示了集合BC(7,4,3)中的所有16个符号。1表示暗,0表示亮。图中的条形码已按字典顺序排列。冒号左边的数字为“条形码”的编号。图1中条形码的在BC(7,4,3)中的编号为4。

输入:输入文件的第一行为数字n,k,m(1<=n,k,m<=30)。第二行为数字s (s<=100)。而后s行中,每行为一个如图1那样描述的集合BC(n,k,m)中的一个“条形码”。

输出:在输出文件中第一行为BC(n.k,m)中“条形码”的个数。而后s行中每一行为输入文件中对应“条形码”的编号。

输入输出示例:

二、分析

题目有两问。容易看出,计数是求序号的基础,因此我们先解决计数问题。原题只给了一个实例,即条形码。为了能用计算机解决该问题,我们必须先建立一个能够很好描述该问题的数学模型。

由于条形码是由黑白相间的且以黑色起头的k块组成,每一块最少含有1条,最多含有m条,k块合起来为n条。因此,一个条形码可以由一个k元组(x1,x2,…,xk)表示,且1≤xi≤m,∑(i=1..k)xi=n。相应地,任意一个满足上述条件的k元组唯一表示一个满足条件的条形码。容易证明,所设的k元组与条形码满足一一对应的关系。满足条件的条形码的个数即为所设的k元组的个数。即方程∑(i=1..k)xi=n,1≤xi≤m的整数解的个数。

最容易想到的是搜索算法1:由于xi的取值范围已经确定,我们可以穷举所有的xi的取值,再检查有多少组解满足∑(i=1..k)xi=n。程序很容易编写,但复杂度却很高,为mk,由于m,k都可能达到30,因此该算法是很低效的。

搜索算法低效的原因是没有很好的利用∑(i=1..k)xi=n这个约束条件,而只将其作为一个判定条件。最容易想到的改进策略是:如要求方程x1+x2+x3=4,1≤x1,x2,x3≤2的整数解的个数。若x1=1,则方程化为x2+x3=4-x1=4-1=3,若x1=2方程化为x2+x3=2。原方程的整数解的个数,正是方程x2+x3=3,x2+x3=2的整数解的个数的和。这样,我们把含3个未知数的方程的整数解个数的问题化为了若干含2个未知数的方程的整数解个数的问题。以此类推,最终可以化为求含一个未知数的方程的整数解个数的问题。容易得出,方程x=n,1≤x≤m的整数解的个数为:当1≤n≤m时,有1个解,否则,有0个解。

容易写出搜索算法2:

Func Count(k,n):LongInt;{∑(i=1..k)xi=n,1≤xi≤m的整数解个数}

begin

if k=1 then if 1≤n≤m then Count:=1

else Count:=0

else for i:=1 to m do Inc(Count,Count(k-1,n-i)){***}

end;

k该程序的时间复杂度仍然为m ,但我们可以通过改进{***}句而将复杂度降低到O(N),

其中N为Count函数的返回值,即方程的整数解个数。具体方法是通过修改循环语句的起始值和终止值:for i:=MinI to MaxI do Inc(Count,Count(k-1,n-i));其中,MinI=Max{1,n-m*(k-1)},MaxI=Min{m,n-k+1}。而方程的整数解个数可以达到6*10甚至更高。显然,这个程序是不高效的。其原因在于所建立的数学模型的抽象程度不高。

我们将方程的整数解个数的模型进一步抽象为:k个在1..m之间的数的和为n的方案数。新的模型与方程的整数解个数的模型似乎没有不同,仅仅是将原模型中未知数xi抽象为k个数。但事实上,这个并不大的变化可以很大程度上的优化程序:我们用F(k,n)表示k个在

1..m之间的数的和为n的方案数,显然有方程式

F(k,n)=∑(i=1..m)F(k-1,n-i)。

和初始条件:若i≠0则F(0,i)=0,F(0,0)=1。

容易写出动态规划程序:

Proc Count;

begin

FillChar (F, Sizeof (F), 0);

F [0,0]: =1;

for i:=1 to n do

for j:=1 to k do

for p:=1 to m do

if i>=p then Inc(F[i,j],F[i-p,j-1])

end;

动态规划的程序的时间复杂度为O(n*k*m)≤30*30*30=27000。

动态规划的特点之一是速度快,另一点便是丰富了运算结果。如本题,我们不仅计算出题设条形码的个数,还计算出了所有由i块组成,每一块最少含有1条,最多含有m条,i块一共为j条的条形码的个数(1≤i≤k,1≤j≤n)。而这些信息可以很方便的解决本题的第二问。

要计算一个条形码的编号,可以先统计在字典顺序中比该条形码小的条形码的个数。这是很容易做到的。具体程序如下:

Func Index (n, k, p: Integer): LongInt;

begin

if k<=1 then Index:=1 else begin

x:=0;

if Odd(p) then begin q:=1;Delta:=1 end else begin q:=m;Delta:=-1 end;

while l[p]<>q do begin

if n>=q then Inc(x,F[n-q,k-1]);

Inc (q,Delta)

end;

8

Inc (x, Index (n-q, k-1, p+1));

Index: =x

end

end;

其中L数组存放的是所读入的条形码的所对应的k元组。如条形码1001110对应的L数组为L[1]=1,L[2]=2,L[3]=3,L[4]=1。该过程的时间复杂度为O(n*k)≤30*30=900

再得到了完美的解答之后,我们再来看看前面的搜索算法。容易看出,搜索算法2可改为:

Func Count(k,n):LongInt;{∑(i=1..k)xi=n,1≤xi≤m的整数解个数}

begin

if F[k,n]=NULL then

if k=0 then F[k,n]:=1

else for i:= Max{1,n-m*(k-1)} to Min{m,n-k+1} do

Inc (F [k, n], Count (k-1, n-i));

Count: =F [k, n]

end;

改进后的算法即为动态规划的递归式写法。此算法可以看作是动态规划算法的改进。因为对决策变量i的初始值和终止值的修正,使得其计算次数较递推写法的动态规划更少。也就是说,我们通过对搜索的算法的改进,得到了同样的动态规划的算法。

那么动态规划与搜索的关系究竟是什么呢,我们再来看另外一个问题:

序关系计数问题 (福建试题)

一、问题描述

用关系‘<’和‘=’将3个数A、B和C依次排列有13种不同的关系:

A<B<C, A<B=C, A<C<B, A=B<C, A=B=C, A=C<B,

B<A<C, B<A=C, B<C<A, B=C<A,

C<A<B, C<A=B, C<B<A。

编程求出N个数依序排列时有多少种关系。

二、分析

<1>.枚举出所有的序关系表达式

我们可以采用回溯法枚举出所有的序关系表达式。N个数的序关系表达式,是通过N个大写字母和连接各字母的N-1个关系符号构成。依次枚举每个位置上的大写字母和关系符号,直到确定一个序关系表达式为止。

由于类似于‘A=B’和‘B=A’的序关系表达式是等价的,为此,规定等号前面的大写字母在ASCII表中的序号,必须比等号后面的字母序号小。基于这个思想,我们很容易写出解这道题目的回溯算法。

算法1,计算N个数的序关系数。

procedure Count(Step,First,Can);

{Step表示当前确定第Step个大写字母;

First表示当前大写字母可能取到的最小值;

Can是一个集合,集合中的元素是还可以使用的大写字母}

begin

if Step=N then begin{确定最后一个字母}

for i:=First to N do if i in Can then Inc(Total);{Total为统计的结果}

Exit

end;

for i:=First to N do{枚举当前的大写字母}

if i in Can then begin{i可以使用}

Count(Step+1,i+1,Can-[i]);{添等号}

Count(Step+1,1,Can-[i]){添小于号}

end

end;

调用Count(1,1,[1..N])后,Total的值就是结果。该算法的时间复杂度是O(N!)

图4-8 N=3时的解答树

<2>.粗略利用信息,优化算法1

算法1中存在大量冗余运算。如图4-8,三个方框内子树的形态是完全一样的。一旦我们知道了其中某一个方框内所产生的序关系数,就可以利用这个信息,直接得到另两个方框内将要产生的序关系数。

显然,在枚举的过程中,若已经确定了前k个数,并且下一个关系符号是小于号,这时所能产生的序关系数就是剩下的N-k个数所能产生的序关系数。

设i个数共有F[i]种不同的序关系,那么,由上面的讨论可知,在算法1中,调用一次Count(Step+1,1,Can-[i])之后,Total的增量应该是F[N-Step]。这个值可以在第一次调用Count(Step+1,1,Can-[i])时求出。而一旦知道了F[N-Step]的值,就可以用Total:=Total+F[N-Step] 代替调用Count(Step+1,1,Can-[i])。这样,我们可以得到改进后的算法1-2。

算法2,计算N个数的序关系数。

procedure Count(Step,First,Can);

{Step,First,Can的含义同算法1}

begin

if Step=N then begin{确定最后一个字母}

for i:=First to N do if i in Can then Inc(Total);{Total为统计的结果}

Exit

end;

for i:=First to N do{枚举当前的大写字母

}

条形码问题 dp+求某个序列在某种排列中的序号的方法的更多相关文章

  1. Poj1159 Palindrome(动态规划DP求最大公共子序列LCS)

    一.Description A palindrome is a symmetrical string, that is, a string read identically from left to ...

  2. leetcode 903. DI序列的有效排列

    题目描述: 我们给出 S,一个源于 {'D', 'I'} 的长度为 n 的字符串 .(这些字母代表 “减少” 和 “增加”.)有效排列 是对整数 {0, 1, ..., n} 的一个排列 P[0], ...

  3. codeforces 429 On the Bench dp+排列组合 限制相邻元素,求合法序列数。

    限制相邻元素,求合法序列数. /** 题目:On the Bench 链接:http://codeforces.com/problemset/problem/840/C 题意:求相邻的元素相乘不为平方 ...

  4. hdu6446 网络赛 Tree and Permutation(树形dp求任意两点距离之和)题解

    题意:有一棵n个点的树,点之间用无向边相连.现把这棵树对应一个序列,这个序列任意两点的距离为这两点在树上的距离,显然,这样的序列有n!个,加入这是第i个序列,那么这个序列所提供的贡献值为:第一个点到其 ...

  5. Poj 2096 (dp求期望 入门)

    / dp求期望的题. 题意:一个软件有s个子系统,会产生n种bug. 某人一天发现一个bug,这个bug属于某种bug,发生在某个子系统中. 求找到所有的n种bug,且每个子系统都找到bug,这样所要 ...

  6. POJ2096 Collecting Bugs(概率DP,求期望)

    Collecting Bugs Ivan is fond of collecting. Unlike other people who collect post stamps, coins or ot ...

  7. POJ 2096 (dp求期望)

    A - Collecting Bugs Time Limit:10000MS     Memory Limit:64000KB     64bit IO Format:%I64d & %I64 ...

  8. POJ 1887:Testing the CATCHER 求递减序列的最大值

    Testing the CATCHER Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 16131   Accepted: 5 ...

  9. [zz]求一维序列的信息熵(香浓熵)的matlab程序实例

    对于一个二维信号,比如灰度图像,灰度值的范围是0-255,因此只要根据像素灰度值(0-255)出现的概率,就可以计算出信息熵.    但是,对于一个一维信号,比如说心电信号,数据值的范围并不是确定的, ...

随机推荐

  1. C/C++用状态转移表联合函数指针数组实现状态机FSM

    状态机在project中使用很的频繁,有例如以下常见的三种实现方法: 1. switch-case 实现.适合简单的状态机. 2. 二维状态表state-event实现.逻辑清晰.可是矩阵通常比較稀疏 ...

  2. hihoCoder 1582 Territorial Dispute 【凸包】(ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛)

    #1582 : Territorial Dispute 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 In 2333, the C++ Empire and the Ja ...

  3. Spring的声明式事务

    1.与hibernate集成 <bean id="sessionFactory" class="org.springframework.orm.hibernate3 ...

  4. eclipse 修改代码后无法生效,需要clean后才能生效的解决办法

    勾选project-->Bulid Automatically选项(自动编译)

  5. ie的兼容方法,条件注释的方法

    1.终极方法:条件注释 <!--[if lte IE 6]> 这段文字仅显示在 IE6及IE6以下版本. <![endif]--> <!--[if gte IE 6]&g ...

  6. HDU3081 Marriage Match II —— 传递闭包 + 二分图最大匹配 or 传递闭包 + 二分 + 最大流

    题目链接:https://vjudge.net/problem/HDU-3081 Marriage Match II Time Limit: 2000/1000 MS (Java/Others)    ...

  7. HDU4513 吉哥系列故事——完美队形II Manacher算法

    题目链接:https://vjudge.net/problem/HDU-4513 吉哥系列故事——完美队形II Time Limit: 3000/1000 MS (Java/Others)    Me ...

  8. dede摘要长度,dedecms摘要限制,dedecms摘要字数

    dede摘要长度,dedecms摘要限制,dedecms摘要字数 如果可以有效控制文章摘要的字数,那么就可以使得页面布局很灵活. 在Dedecms中,在列表页调用文章摘要的方法主要有: 1:[fiel ...

  9. 解决IE浏览器部分版本不支持background-size属性问题

    background-size是CSS3新增的属性,现在有很多浏览器都支持CSS3了.但是IE浏览器有些版本还是不支持,比如IE8,IE9也有些CSS3的属性会支持,但是有些也不支持.在这里就了解一下 ...

  10. 并不对劲的p4449于神之怒加强版

    题目大意 给定\(t,k(t\leq2000,k\leq5*10^6)\) \(t\)组询问,每组给出\(n,m(n,m\leq5*10^6)\)求$\sum_{i=1}^n \sum_{j=1}^m ...