今天的考试完全考水了,也不知道原因,各种题目看不清楚。莫名其妙的把所有的分都丢了、
第一题
基因重组 【问题描述】 工程师Enigma在研究项目遇到一个有关基因重组方面的难题。 众所周知,一个基因可以被认为是一个序列,包括4个核苷酸,可以由4个字母简单标记:A,C,G,T。 Enigma已经得到了一个基因,标记为"ATCC",诸如此类,他想将此基因重新组合产生一个新的,比如“CTCA”,他可以进行的有两种操作: (1)将前两个字母交换,或者 (2)将第一个字母移到最后。 举例说明,可以使用第2种操作将“ATCC”变成“TCCA”,又可以使用第1种操作将“TCCA”变为“CTCA”。
你的任务是编程帮助Enigma找到完成一个基因重组所需的最少操作次数。 【输入格式】 第一行有一个整数N,表示基因序列的长度(1<=N<=12),第二行有一个字符串,表示该基因序列,第三行是另一个字符串,即Enigma希望得到的基因序列。 对于每一个字母,它在两个字符串中出现的次数相同。 【输出格式】 一行,即最小操作次数。 【样例】 gene.in 4 ATCC CTCA gene.out 2
很明显的搜索,只有4^12种情况,但是由于空间给的小,只能用哈希来优化宽搜,至今还有两个点没过。
genea
1 /** 2 *Prob : genea 3 *Data : 2012-7-3 4 *Sol : Search + hash 5 */ 6 7 #include <cstdio> 8 #include <queue> 9 #include <string> 10 #include <iostream> 11 #include <cstring> 12 13 #define MaxS 16777216 14 15 using namespace std; 16 17 struct node { 18 int k,s; 19 }; 20 queue <node> list; 21 22 int n; 23 bool v[MaxS]; 24 char tma[20],tmb[20]; 25 int a[20],b[20],tm1[20],tm2[20],tmp[20]; 26 27 void Change_init() 28 { 29 int tmp = 1; a[n] = b[n] = 0; 30 for (int i=n-1; i>=0; i--) { 31 a[n] += a[i]*tmp; 32 b[n] += b[i]*tmp; 33 tmp *= 4; 34 } 35 } 36 void Change(int *a) 37 { 38 int tmp = 1; a[n] = 0; 39 for (int i=n-1; i>=0; i--) { 40 a[n] += a[i]*tmp; 41 tmp *= 4; 42 } 43 } 44 void Change1(int *tm1) 45 { 46 int tmp = tm1[0]; 47 tm1[0] = tm1[1]; tm1[1] = tmp; 48 } 49 void Change2(int *tm2) 50 { 51 int tmp = tm2[0]; 52 for (int i=0; i<n-1; i++) 53 tm2[i] = tm2[i+1]; 54 tm2[n-1] = tmp; 55 } 56 void Change3(int s,int *tmp) 57 { 58 int num = n; 59 memset(tmp,0,sizeof(tmp)); 60 tmp[n] = s; 61 while (s) { 62 num--; 63 tmp[num] = s%4; 64 s /= 4; 65 } 66 } 67 68 int Search() 69 { 70 memset(v,false,sizeof(v)); 71 node now,nowtm1,nowtm2; 72 now.k = 0; now.s = a[n]; 73 list.push(now); 74 v[a[n]] = true; 75 76 while (!list.empty()) { 77 now = list.front(); 78 list.pop(); 79 if (now.s == b[n]) 80 return now.k; 81 Change3(now.s,tmp); 82 for (int i=0; i<=n; i++) 83 tm2[i] = tm1[i] = tmp[i]; 84 Change1(tm1); Change(tm1); 85 Change2(tm2); Change(tm2); 86 if (!v[tm1[n]]) { 87 v[tm1[n]] = true; 88 nowtm1.k = now.k+1; 89 nowtm1.s = tm1[n]; 90 list.push(nowtm1); 91 } 92 if (!v[tm2[n]]) { 93 v[tm2[n]] = true; 94 nowtm2.k = now.k+1; 95 nowtm2.s = tm2[n]; 96 list.push(nowtm2); 97 } 98 } 99 100 } 101 102 int main() 103 { 104 freopen("genea.in","r",stdin); 105 freopen("genea.out","w",stdout); 106 107 scanf("%d\n",&n); 108 scanf("%s\n",tma); 109 scanf("%s\n",tmb); 110 for (int i=0; i<n; i++) { 111 if (tma[i]=='A') a[i] = 0; 112 if (tma[i]=='C') a[i] = 1; 113 if (tma[i]=='G') a[i] = 2; 114 if (tma[i]=='T') a[i] = 3; 115 116 if (tmb[i]=='A') b[i] = 0; 117 if (tmb[i]=='C') b[i] = 1; 118 if (tmb[i]=='G') b[i] = 2; 119 if (tmb[i]=='T') b[i] = 3; 120 } 121 Change_init(); 122 123 int ans; 124 if (a[n]==b[n]) ans = 0; 125 else ans=Search(); 126 127 printf("%d\n",ans); 128 129 fclose(stdin); fclose(stdout); 130 return 0; 131 }
第二题
旅行 【问题描述】 一天,TZD想从0号村庄游历到n-1号村庄,这n个村庄由m条有向路连接,某两个村庄间可能有多条路相连。 当他在处于同一个强连通分支中的两个村庄间游历时,他可以租辆自行车来骑着,否则的话,就只能步行了。
说的再详细点,对于从村庄a到村庄b的每一条路,如果这两个村庄处在同一个强连通分支中,(这意味着你可以从a到达b,也可以从b到达a)
那么你可以租一辆自行车来骑行,所用时间为time[a][b]分钟,否则你必须步行通过,所用时间为2*time[a][b]分钟。 TZD想尽快到达目的地,他希望在此过程中步行的路不超过k条。现在你需要为他计算出:在满足步行的路不超过k条的情况下,到达目的地所用的最短时间。 【输入格式】 输入文件包含若干组测试数据。 每一组数据的第一行有三个整数:n,m,k,含义如上所述(1<=n<=100,0<=m<=100,000,0<=k<=10)。 接下来有m行,每行有3个整数a,b,time[a][b],表示从村庄a至村庄b有一条路,骑自行车需time[a][b]分钟,而步行则需2*time[a][b]分钟。
(0<=a,b<=n-1,0<time[a][b]<=10,000) 输入文件以单独的一行3个0表示结束。 【输出格式】 对于每一组测试数据,在一行输出其答案,如果TZD无法在步行不超过k条路的情况下到达目的地,则输出-1。 【样例】 travel.in 5 6 3 0 1 1 0 2 2 2 1 1 1 3 3 3 1 3 3 4 2 0 0 0 travel.out 9
很明显的可以用Floyd求连通图,然后用dis[i][k]来表示从0到点i,走了k次,所用的最短时间,用spfa求得最短路,然后在松弛操作的时候判断是否是强连通分量,从而可以看是用来更新dis[j][k+1]还是dis[j][k],其他的就是最简单的spfa了,也可以拆点来做
这道题悲剧的看错数据了,应该是多组数据,但是我是按一组写的,所以一分都没拿到,但是加上循环后就好了……
travel
1 /** 2 *Prob : travela 3 *Data : 2012-7-3 4 *Sol : Floyd+spfa 5 */ 6 7 #include <cstdio> 8 #include <cstring> 9 #include <algorithm> 10 11 #define MaxN 120 12 #define MaxK 20 13 #define MaxList 1200000 14 #define oo 1000000000 15 16 using namespace std; 17 18 struct node { 19 int n,k; 20 } list[MaxList]; 21 22 int n,k; 23 int a[MaxN][MaxN]; 24 int dis[MaxN][MaxK]; 25 bool map[MaxN][MaxN]; 26 27 void Floyd() 28 { 29 for (int k=0; k<n; k++) 30 for (int i=0; i<n; i++) 31 for (int j=0; j<n; j++) 32 { 33 if (k==i||k==j||i==j) continue; 34 if (map[i][k]&&map[k][j]) 35 map[i][j] = map[i][j] || true; 36 } 37 } 38 39 void Spfa() 40 { 41 bool v[MaxN][MaxK]; 42 memset(v,false,sizeof(v)); 43 v[0][0] = true; 44 list[1].n = list[1].k = 0; 45 int open = 1,closd = 0; 46 for (int i=0; i<n; i++) 47 for (int j=0; j<=k; j++) 48 dis[i][j] = oo; 49 dis[0][0] = 0; 50 51 while (closd<open) { 52 int nown = list[++closd].n; 53 int nowk = list[closd].k; 54 v[nown][nowk] = false; 55 for (int tn=0; tn<n; tn++) { 56 //???? 57 if (map[nown][tn]&&map[tn][nown]) { 58 //?б? 59 if (a[nown][tn]) 60 if (dis[nown][nowk]+a[nown][tn]<dis[tn][nowk]) { 61 dis[tn][nowk] = dis[nown][nowk]+a[nown][tn]; 62 if (!v[tn][nowk]) { 63 v[tn][nowk] = true; 64 list[++open].n = tn; 65 list[open].k = nowk; 66 } 67 } 68 } 69 //?????? 70 if (map[nown][tn]&&(!map[tn][nown])) { 71 if (nowk == k) break; 72 //?б? 73 if (a[nown][tn]) 74 if (dis[nown][nowk]+2*a[nown][tn]<dis[tn][nowk+1]) { 75 dis[tn][nowk+1] = dis[nown][nowk]+2*a[nown][tn]; 76 if (!v[tn][nowk+1]) { 77 v[tn][nowk+1] = true; 78 list[++open].n = tn; 79 list[open].k = nowk+1; 80 } 81 } 82 } 83 } 84 85 } 86 } 87 88 int main() 89 { 90 freopen("travela.in","r",stdin); 91 freopen("travela.out","w",stdout); 92 93 int m,x,y,z; 94 95 96 scanf("%d%d%d",&n,&m,&k); 97 98 while (!(n==0&&m==0&&k==0)) { 99 100 memset(map,false,sizeof(map)); 101 for (int i=0; i<n; i++) 102 for (int j=0; j<n; j++) 103 a[i][j] = oo; 104 for (int i=1; i<=m; i++) { 105 scanf("%d%d%d",&x,&y,&z); 106 a[x][y] = min(z,a[x][y]); 107 map[x][y] = true; 108 } 109 110 Floyd(); 111 112 Spfa(); 113 114 int ans = oo; 115 for (int i=0; i<=k; i++) 116 ans = min(ans,dis[n-1][i]); 117 118 if (ans==oo) ans = -1; 119 120 printf("%d\n",ans); 121 122 scanf("%d%d%d",&n,&m,&k); 123 } 124 125 fclose(stdin); fclose(stdout); 126 return 0; 127 }
第三题
DNA重组 【问题描述】 有一次,Z国的研究人员从一些原始的有机组织中提取出了一条DNA链,他们的任务是通过对旧的DNA进行重组,进而生成一条新的DNA链。
他们投资了一个计划,用如下的方式使用基因剪和基因胶。 首先,他们将DNA链在某些连接点处断开,这样整个链就会就变成一些DNA片断,然后他们再决定是保留还是丢弃这些片断,如果保留这些片断,他们将利用基因胶把它们粘成一条新链。
注意,粘的过程中不能打乱这些片断原来的顺序。
在整个过程中他们不得不非常小心地确定这些片断的去留。 当然了,做这些操作是需要花费代价的,每一个“剪开”的操作代价为1,每一个“粘合”操作花费代价也为1。
请你写一个程序计算:如果存在一种方式生成一个新的DNA链,那么所需操作的最小代价是多少? 请看下面的例子: 旧的DNA:A---T---A---C---C---G 剪开后: A T A---C C---G 保留: T C---G 新的DNA链: T---C---G 花费代价:4 【输入格式】 输入文件第一行有一个正整数T,表示接下来测试数据的个数。
每一个测试数据包含两行,每行一个字符串,第一个字符串表示旧的DNA链,第二行为新的DNA链。每个字符串都由"A","T","C"和"G"组成,每一个字符串的长度均不超过3000。 【输出格式】 对于每一个测试数据,输出其最小花费,如果没办法生成新的DNA链,则输出“-1”。 【样例】 dna.in 2 ATACCG TCG ATACCG CTG dna.out 4 -1
动态规划,O(n^3)的算法好写,就是按照类似于最长公共子序列的方法,不过可以用两个数组来动归优化,很好写,但是不太好想,具体的就是
f[i][j][0]原始串的前i个和目标串的前j个匹配了,a[i]==b[j]
f[i][j][1]原始串的前i个和目标串的前j个匹配了,a[i]!=b[j] (即结尾是多余的)
最终的答案就是min(f[n][m][0],f[n][m][1]+1)
状态转移方程看代码
dna
1 /** 2 *Prob : dna 3 *Data : 2012-7-3 4 *Sol : dp 5 */ 6 7 #include <cstdio> 8 #include <string> 9 #include <cstring> 10 #include <iostream> 11 #include <algorithm> 12 13 #define MaxN 3010 14 #define oo 100000000 15 16 using namespace std; 17 18 int T; 19 string a,b; 20 int f[MaxN][MaxN][2]; 21 22 void dp() 23 { 24 memset(f,127,sizeof(f)); 25 26 for (int i=0; i<a.length(); i++) 27 for (int j=0; j<=i; j++) 28 { 29 if (i>0) f[i][j][1] = min(f[i-1][j][0],f[i-1][j][1]); 30 31 if (a[i]!=b[j]) { 32 f[i][j][0] = oo; continue; } 33 if (a[i]==b[j]&&i==j&&j==0) { 34 f[i][j][0] = 0; continue; } 35 if (a[i]==b[j]&&i>0&&j==0) { 36 f[i][j][0] = 1; continue; } 37 if (j>0&&i>0) 38 f[i][j][0] = min(f[i-1][j-1][0],f[i-1][j-1][1]+3); 39 } 40 } 41 42 int main() 43 { 44 freopen("dna.in","r",stdin); 45 freopen("dna.out","w",stdout); 46 47 scanf("%d",&T); 48 int ans; 49 for (int i=1; i<=T; i++) { 50 cin>>a; cin>>b; 51 dp(); 52 ans = min(f[a.length()-1][b.length()-1][0],f[a.length()-1][b.length()-1][1]+1); 53 if (ans>=oo) ans = -1; 54 printf("%d\n",ans); 55 } 56 57 58 fclose(stdin); fclose(stdout); 59 return 0; 60 }
下次调整心态,争取考好。