問題一:郵票封面設計
問題描述:
給定一個信封,最多隻允許貼 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