本解题报告由 Ceeji 编著,转载请注明出处。
[题目描述]
Genotype 是一个有限的基因序列。它是由大写的英文字母A-Z组成,不同的字母表示不同种类的基因。一个基因可以分化成为一对新的基因。这种分化被一个定义的规则集 合所控制。每个分化的规则可以用三个大写字母A1A2A3表示,含义为基因A1可以分化成A2A3。我们用S代表特种基因,繁殖genotype是从特种 基因序列开始。根据给定的规则,它由被选择控制规则对基因不断进行繁殖而成。
任务
从文本文件GEN.IN 读入一个定义的规则集和一个想生成的genotypes 单词序列。对每一个给定的 genotype,根据给定的分化规则,检查是否它能从某一个确定特种基因序列生成,如果能,找到最小的序列长度,将结果写入文本文件GEN.OUT。
输入
在文件GEN.IN 的第一行有一个整数n, 1 <= n <= 10000. 下面n 每一行为一个分化规则. 这些规则都由包含A – Z的三个大写字母组成.
接下来有一个整数k, 1 <= k <= 10000. 接下来的k 行有一个 genotype. Genotype由没有空格的单词组成,最多100 个英文大写字母.
输出
在文件GEN.OUT中有k行,在第I行应写入: 一个正整数――需要生成第I个genotypes的最小长度;或者单词 NIE, 如果不能生成对应的genotype。
GEN.IN:
6
SAB
SBC
SAA
ACA
BCC
CBC
3
ABBCAAABCA
CCC
BA
GEN.OUT:
3
1
NIE
解题思路
本解题报告由 Ceeji 编著,转载请注明出处。
这道题是典型的双重动态规划。
第一步: 由题意可以知道,分化具有阶段性和规律性,这正是动态规划的特征。由于要求的是最少的特等基因数目,所以考虑一次分化的情况。要一次分化到的串设为T,假 设字符Ch1能够一次分化成Ch2和Ch3,并且Ch2能够一次分化成从第p位到第i位的字符串,而Ch3能够一次分化成从第i+1位到第q位的字符串, 那么必有Ch1能够一次分化到从第p位到第q位的字符串。如Ch1=S,Ch2=A,Ch3=B时,S->AB,A->BC,B-> CD,则 S -> BCCD。需要穷举 Ch2 和 Ch3,所以使用了数组模拟链表。动态转移方程如下:
can[ch1,p,q]:= can[ch2,p,i] and can[ch3,i+1,j]
其中ch1能够一次分化到ch2和ch3。这里的i作为划分点,再次体现了二分的思想。
由于本人的程序中使用了记忆化搜索的思想,所以 can 设置为了数值型,初始值为 0,能够一次分化为 1,否则为 2。
第二步:由上面的思路可以得出任意一个字符Ch能否一步分化到字串T的任意一段。那么,如何得出指定字串至少由S多少次分化可以得到呢?可以再次使用动态规划思想。假设DP[i]表示T的前i位字串至少需要的特等基因的数目,那么易得动态转移方程如下:
DP[i]:= min {DP[j] + 1};
其中的 j 符合
can['S',j+1,i]=true
(或,记忆化搜索中使用
can['S',j+1,i]=1
)
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | //By Ceeji //From: HAOI Test 4 - Progblem 1 : Genotype //Date: 2009-4-4 //Type: DP //States: Accepted Program geno; Type node=record ch1,ch2:char; next:longint; end; Const inf='gen.in'; ouf='gen.out'; maxl=100; maxn=10000; Var n,k,u,m:longint; // n : number of strings of input f:array['A'..'Z']of longint; hz:array[1..10000]of node; can:array['A'..'Z',1..maxl,1..maxl]of integer; dp:array[1..maxl]of longint; str:string; Procedure add(ch1,ch2,ch3:char); begin hz[m].ch1:=ch2; hz[m].ch2:=ch3; hz[m].next:=f[ch1]; f[ch1]:=m; end; //Init the program Procedure init; var i:longint; ch1,ch2,ch3:char; begin assign(input,inf); reset(input); assign(output,ouf); rewrite(output); fillchar(f,sizeof(f),0); fillchar(hz,sizeof(hz),0); readln(n); m:=0; for m:=1 to n do begin readln(ch1,ch2,ch3); add(ch1,ch2,ch3); end; readln(k); end; //Check whether it can be creeated from the parent. The double DP. Function check(c:char; p,q:longint):boolean; var i,k:longint; begin if can[c,p,q]<>0 then exit(can[c,p,q]=1); if (c=str[p])and(p=q) then begin can[c,p,q]:=1; exit(true); end; for i:=p to q-1 do begin k:=f[c]; while k<>0 do begin if (check(hz[k].ch1,p,i))and(check(hz[k].ch2,i+1,q)) then begin can[c,p,q]:=1; exit(true); end; k:=hz[k].next; end; end; can[c,p,q]:=2; exit(false); end; //Do a simple work Procedure doit; var i,j,len:longint; c:char; begin readln(str); len:=length(str); fillchar(can,sizeof(can),0); for i:=1 to len do dp[i]:=1000000; dp[0]:=0; for i:=1 to len do begin for j:=0 to i-1 do begin if (dp[i]>dp[j]+1)and(check('S',j+1,i))then dp[i]:=dp[j]+1; end; end; if dp[len]>=1000000 then writeln('NIE') else writeln(dp[len]); end; //End the output Procedure endPro; begin close(output); end; {main} begin init; for u:=1 to k do doit; endpro; end.{main} |