题目

条形码是一种由亮条(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. putty software caused connection abort

    错误现象:在非常短的时间内就失去连接.并报"Software caused connection abort" 解决的方法:首先得排除是网络不是不通畅.假设在局域网中要确定IP没有 ...

  2. Codeforces Round #422 (Div. 2) A. I'm bored with life 暴力

    A. I'm bored with life     Holidays have finished. Thanks to the help of the hacker Leha, Noora mana ...

  3. ECMAScript学习笔记

    1. ECMAScript不存在块级作用域,因此在循环内部定义的变量,在循环外也是可以访问的 eg: var count =10; fpr(var i=0; i<count; i++){ ale ...

  4. iOS 摇一摇功能的实现

    在 UIResponder中存在这么一套方法 - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event __OSX_A ...

  5. pod导入第三方库头文件不能自动联想的解决方法

    使用了一段时间CocoaPods来管理Objective-c的类库,方便了不少.但是有一个小问题,当我在xcode输入import关键字的时候,没有自动联想补齐代码的功能,需要手工敲全了文件名,难以适 ...

  6. 初探linux子系统集之led子系统(一)【转】

    本文转载自:http://blog.csdn.net/eastmoon502136/article/details/37569789 就像学编程第一个范例helloworld一样,学嵌入式,单片机.f ...

  7. ExtJS常用代码集合

    ExtJS常用代码集合,包括弹出提示框,登陆框,树状结构等等.​1. [代码]弹出提示框     <html>    <head>        <title>Ge ...

  8. poj 2771 Guardian of Decency 解题报告

    题目链接:http://poj.org/problem?id=2771 题目意思:有一个保守的老师要带他的学生来一次短途旅行,但是他又害怕有些人会变成情侣关系,于是就想出了一个方法: 1.身高差距   ...

  9. Html5--6-46 渐变效果

    Html5--6-46 渐变效果 学习要点 掌握线性渐变和径向渐变的使用 线性渐变: 属性:linear-gradinet(开始位置 角度,起始颜色,终止颜色 ) 开始位置:渐变开始的位置,属性值可以 ...

  10. 后缀自动机SAM BZOJ 2806

    终于遇到了一道后缀数组不能过 一定要学SAM的题... (看了半个下午+半个上午) 现在总结一下(是给我自己总结..所以只总结了我觉得重要的 .. 看不太懂的话可以To   http://blog.c ...