问题一:邮票封面设计
问题描述:
给定一个信封,最多只允许贴 N张邮票,计算在给定K(N+K<=40) 种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大max,使得1__max之间的每一个邮资值都能得到。
例如:N=3,K=2,如果面值分别为1分、4分,则在1分__6分之间的每一个邮资值都能得到(当然还有8分、9分、和12分):如果面值分别为1分、3分则在1分–7分之间的每一个邮资值都能得到。可以验证当N=3,K=2时,7分就是可以得到的连续的邮资最大 值,所以max=7,面值分别为1分、3分。
样例:
INPUT
N=3 K=2
OUTPUT
1 3
MAX=7
运行示列:
n=7 n=7
k=3 k=4
1 8 13 1 5 24 37
max=69 max=165
n=10 n=5
k=3 k=5
1 10 26 1 4 9 31 51
max=146 max=126
邮票问题程序清单:(2000级1班舒新明编制)
#define n 32767
#define m0 20
#define m 1500
int x[m0],y[m+1],bx[m0],max=0,n0,k0;
void stamp(int i,int r)
{ int z[m+1],j,k;
for(j=0;j<=x[i-2]*(n0-1);j++)
if(y[j]<n0)
for(k=1;k<=n0-y[j];k++)
if(y[j]+k<y[j+x[i-1]*k])
y[j+x[i-1]*k]=y[j]+k;
while(y[r]<n) r++;
if(i>k0)
{if(r-1>max)
{max=r-1;
for(j=1;j<=k0;j++)
bx[j]=x[j];
} return;
}
for(k=1;k<=m;k++) z[k]=y[k];
for(j=x[i-1]+1;j<=r;j++)
{ x[i]=j;
stamp(i+1,r);
for(k=1;k<=m;k++) y[k]=z[k];
}
}
main()
{ int i;
printf(“Input: n0,k0:/n”);
scanf(“%d%d”,&n0,&k0);
for(i=1;i<=m;i++) y[i]=n;
for(i=0;i<=k0;i++) x[i]=0;
x[1]=1; y[0]=0;
stamp(2,1);
printf(“Output:/n”);
for(i=1;i<=k0;i++)
printf(“%4d”,bx[i]);
printf(“/nmax=%d/n”,max);
}
七、单词接龙
单词接龙是一个与我们经常玩的成语接龙相似的游戏,现在我们已知一组单词,且给定一个开始字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时其重合部分合为一部分,例如:beast和astonish, 如果接成一条龙则变为beastonish,另外相邻两部分不能存在包含关系,例如at 和atide间不能相连。
输入:输入的第一行为一个单独的整数n(n<=20)表示单词数,以下n行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。
输出: 只需输出以此字母开头的最长的“龙”的长度。
样例:
INPUT
5
at
touch
cheat
choose
tact
a
OUTPUT
23
(连接成的“龙”为:atoucheatactactouchoose)
问题分析:
本题主要是要考搜索,至于是使用宽度优先搜索还是深度优先搜索,因该是对哪种算法熟悉就采用哪种算法。实际上两种算法各有其优缺点,宽度优先搜索速度快,对存储空间要求较高;而深度优先搜索则不存在内存不足的问题,但运行速度相对较慢。
数据结构:
在本题的搜索中存在对两个单词是否可以连接的判断,因此用一个邻接矩阵做记录。程序最后要输出最长的龙的长度,则我们不仅要知道某一个单词是否可以与龙尾的单词连接,而且还要知道如果该单词可以与龙尾的单词相连的话,将之连接在龙尾后,龙的长度增加了多少。因此邻接矩阵定义如下: 0 单词j不能连接在单词i之后
link[i][j]= len 单词j能连接在单词i之后,
接上之后龙的长度增加len
由于只要求计算出最长的龙的长度,因此队列元素只需要记录龙的长度,而不需要记录龙对应的字符串,这样可以节省大量的存储空间。另外题目中规定每个单词最多使用两次,因此搜索时要记录下当前结点中的龙是由那些单词构成的,每个单词分别被用几次。
其数据结构定义如下:
define struc node
{ int state[max]; 记录结点中的龙的单词构成情况
int last; 记录结点中的龙的最后一个单词序号
int len ;记录结点中龙的长度
}
算法分析:
计算Link数组的算法如下:
for(i=1;i<=n;i++)
fro(j=1;j<=n;j++)
{ Link[i][j]=0;
false=0; len=1;/*len表示单词i与单词j重叠长度*/
while((len<strlen(word[i]))
&&(len<strlen(word[j]))&&(!find))
{ /*判断单词i 从p1位置开始到最后一个字母为止的子串 是否单词j的开头部分长为len的子串相同*/
p1=strlen(word[i])-len+1; p2=1;
while(p1<=strlen(word[i]))&&(word[i][p1]=word[j][p2)
{ p1=p1+1; p2=p2+1;}
if(p1>strlen(word[i])find=1; else len=len+1;
}
if find linkk[i][j]=strlen(word[j])-len;
}
首先将所有满足开头字母为指定字母的单词放进队列,作为初始队列,然后每次从队头取一个元素进行扩展,将扩展出的新结点(接上一个新单词之后的龙)增加在队尾,直到不再有新结点为止。最后扫描整个队列,将队列中最长的龙的长度求出即可。
以下给出宽度优先搜索的算法:
hean=1; /*hean 为队头指针,tail指向队尾*/
while(head<tail)
{ for(i=1;i<=n;i++)
/*尝试在队头元素之后接上第i 个单词*/
if(q[head].state[i]<2)&&(link[q[head].last][i]>0)
{ for(j=1;j<=n;j++)
q[tail].state[j]=q[head].state[j];
q[tail].state[i]=q[tail].state[i]+1;
q[tail].last=i;
q[tail].len=q[head].len+link[q[head].last][i];
tail++; head++;
}
方格取数
设有N*N 的方格图(N<=8),我们将其中的某些方格中填入正整数,二其它的方格中放入数字0。如图所示:
向右
0
0
0
问题分析:
该题给出的问题规模是n+k<=40,是否采用动态规划;实际上,本题具有明显的后效性,前面方案的不同使得构成某面值的邮票数不同,动态规划是不成立的。一般认为解决这类问题的方法只有递归搜索,所能解决问题规模也远远达不到题目的要求(n和k基本不能同时超过6),这可以说是条件中的一个陷阱。即使对于较小的n和k,要想出解也要具有正确的思路才行。
首先面值为1的邮票是必须的,可以固定; 此后每加进一种面值都要把当前所能取得的金额记录下来,通过k-1次的添加得到一种方案,穷举所有的方案得到最优解。这就是算法的基本框架。
数据结构:
在搜索的过程中,对数据的记录方式很关键。如果仅仅记录 一个金额能否被达到,则这样的记录基本没有用,应为下一次添加邮票时不知道能否从这个金额出发再加上一张新邮票,必须全部重算,浪费了大量的时间;所以记录最少要用几张邮票达到一个面值是比较方便的,它有利于添加新邮票时迅速判断可行性。另外搜索一种邮票面值的起点也要注意,应当是从上一个面值加1直到当前连续取得的最大金额加1。在这个范围之外的解都可以不考虑:过大导致不连续;小于前一面值时可将前后面值交换,仍得到增序的邮票面值序列。程序中用一维数组s 记录搜索过程中每种邮票的面值,数组result记录当前最优解,数组d记录当前请况下每个面值最少要用几张邮票才能达到,maxc表示当前情况下不能达到的最小面值。
程序清单:
一、字符串编辑
从键盘输入一字符串(长度<=40 个字符),并以’.’结束。
例如:‘ This is a book.’ 现对该字符串进行编辑,
编辑功能有:
D:删除一个字符,命令的方式为:
D a其中a为被删字符
例如:D s表示删除字符’s’,若有多个’s’,
则删除第一次出现的,如上例中删除的结果
为:’Thi is a book.’
I:插入一个字符,命令格式为:
I a1 a2 其中a1表示插入到指定字符前面,a2表示将要插入的字符。
例如:I s d 表示在指定字符‘s’的前面插入字符‘d’,若原串中有多个‘s’,则插入在最后一个字符的前面。
如上例中:
原 串:‘This is a book.’
插入后:’This ids a book.’
R:替换一个字符,命令格式为:
R a1 a2其中a1为被替换字符,a2为替换的字符,若在原串中有多个a1,则应全部替换。
例如: 原串:’This is a book.’
输入命令: R o e
替换后的字符串为: ‘This is a beek.’
在编辑过程中,若出现被指定的字符不存在时,则给出提示信息。
三、代数表达式的定义如下:
数表达式 项
+ –
项 因子
* ?
因子 字母
(代数表达式)
字母 a
b
c
例如,下面式子是合法的代数表达式:
a;
a+b*(a+c);
a*a/(b+c);
下例式子是不合法的代数表达式:
ab;
a+b*(c+d);{因子中无字母d}
程序要求:
输入一个字符串,以‘;’结束,(‘;’本身不是代数表达式中字符,
仅作为结束符号)。
输出:若表达式正确,则输出’OK’;
若表达式不正确,则输出‘而,及错误内型。
错误类型约定;1、式子中出现不允许出现的字符;
2、括号不配对;
3、其它错误。
例如:输入 a+(b);
输出 OK
例如:输入a+9b+c*a;
输出 error 2