Monthly Archives: November 2014

POJ 2503-Babelfish

POJ 2503-Babelfish Babelfish Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 33433 Accepted: 14347 Description You have just moved from Waterloo to a big city. The people here speak an incomprehensible dialect of a foreign language. Fortunately, you have a dictionary to help you understand them. Input Input consists of up to 100,000 dictionary entries, followed by a blank line, followed by a message of up to 100,000 words. Each dictionary entry is a line containing an English word, followed by a space and a foreign language word. No foreign word appears more than once in the dictionary. The message is a sequence of words in the foreign language, one word on each line. Each word in the input is a sequence of at most 10 lowercase letters. Output Output is the message translated to English, one word per line. Foreign words not in the dictionary should be translated as “eh”. Sample Input dog ogday cat atcay pig igpay froot ootfray loops oopslay atcay ittenkay oopslay Sample Output cat eh loops Hint Huge input and output,scanf and printf are recommended. Source Waterloo local 2001.09.22


水题。题目说明了“No foreign word appears more than once in the dictionary”,又要快速查找,马上想到用MAP来当字典,然后查找。这题稍微有点麻烦是的输入输出,因为题目没有给出具体的输入结束标志,需要自己判断,输入的又是字符串,且含有空格空行等,虽然题目hint建议用printf或scanf,但是我选择了用C++的getline,看起来更加优雅。 getline的优点是一次读入一行,不管有没有空格,而且它还能读入空行,所以我们可以使用下面的代码来读取一行并判断跳出循环条件: [cpp] while(getline(cin,s)) { if(s=="") break; } [/cpp] 其他的就是常规的字符串处理,map查找输出。具体代码如下: [cpp] #include<iostream> #include<string> #include<map> #include<vector> using namespace std; int main() { string s; map<string,string> mss; vector<string> vs; while(getline(cin,s)) { if(s=="") break; int blankPos=s.find(" ");//找到空格位置 mss[s.substr(blankPos+1)]=s.substr(0,blankPos);//插入map } while(getline(cin,s)) { if(s=="") break; if(mss.find(s)!=mss.end()) { vs.push_back(mss[s]); //cout<<mss[s]<<endl; } else //cout<<"eh"<<endl; vs.push_back("eh"); } int v_size=vs.size(); for(int i=0;i<v_size;i++) { cout<<vs[i]; if(i!=v_size-1) cout<<endl; } return 0; } [/cpp] 本代码提交AC,用时1000MS,内存14648K,效率比较低,如果使用scanf和printf估计会好些。]]>

POJ 2151-Check the difficulty of problems

POJ 2151-Check the difficulty of problems Check the difficulty of problems Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 5066 Accepted: 2238 Description Organizing a programming contest is not an easy job. To avoid making the problems too difficult, the organizer usually expect the contest result satisfy the following two terms: 1. All of the teams solve at least one problem. 2. The champion (One of those teams that solve the most problems) solves at least a certain number of problems. Now the organizer has studied out the contest problems, and through the result of preliminary contest, the organizer can estimate the probability that a certain team can successfully solve a certain problem. Given the number of contest problems M, the number of teams T, and the number of problems N that the organizer expect the champion solve at least. We also assume that team i solves problem j with the probability Pij (1 <= i <= T, 1<= j <= M). Well, can you calculate the probability that all of the teams solve at least one problem, and at the same time the champion team solves at least N problems? Input The input consists of several test cases. The first line of each test case contains three integers M (0 < M <= 30), T (1 < T <= 1000) and N (0 < N <= M). Each of the following T lines contains M floating-point numbers in the range of [0,1]. In these T lines, the j-th number in the i-th line is just Pij. A test case of M = T = N = 0 indicates the end of input, and should not be processed. Output For each test case, please output the answer in a separate line. The result should be rounded to three digits after the decimal point. Sample Input 2 2 2 0.9 0.9 1 0.9 0 0 0 Sample Output 0.972 Source POJ Monthly,鲁小石


这题纯粹的数学概率题,不太好做。题目大意是:有m个问题,t个队,要求冠军至少解决n个问题,所有队至少解决1个问题,问这样的概率是多少。对于题目中给的样例,我们可以手算得出,分3种情况:a.第一个队是冠军,第二个队只解出一题0.9*0.9*1*0.1=0.081;b.第二个队是冠军,第一个队只解出一题(0.9*0.1+0.1*0.9)*1*0.9=0.162;c.两个队都是冠军,也就是并列冠军0.9*0.9*1*0.9=0.729;把这三种情况加起来就是0.972。 但是如果m,t的数字很大,问题就没那么简单了,解题思路比较难想到,可以参考以下两篇博客: 比较难理解的是概率的转化:
要求:每队至少解出一题且冠军队至少解出N道题的概率。
由于冠军队可以不止一队,即允许存在并列冠军。 则原来的所求的概率可以转化为:每队均至少做一题的概率P1 减去 每队做题数均在1到N-1之间的概率P2。 如果理解了上面那句话,使用概率DP就很容易写出代码: [cpp] #include<iostream> #include<cstdio> using namespace std; int m,t,n; double p[1001][31];//p[i][j]第i个队解出第j题的概率 double dp[1001][31][31];//dp[i][j][k]第i个队在前j个题中解出k个题的概率dp[i][j][k]=dp[i][j-1][k-1]*p[j][k]+dp[i][j-1][k]*(1-p[j][k])分第j个题能和不能解出来 double s[1001][31];//s[i][k]第i个队最多解出k个题的概率s[i][k]=dp[i][M][0]+dp[i][M][1]+…+dp[i][M][k] void solve() { for(int i=1;i<=t;i++) { dp[i][0][0]=1.0;//初始化,第i支队伍前0道题做对0道的概率为1 for(int j=1;j<=m;j++) { for(int k=0;k<=j;k++) { dp[i][j][k]=dp[i][j-1][k]*(1-p[i][j]); if(k!=0) dp[i][j][k]+=dp[i][j-1][k-1]*p[i][j]; } } s[i][0]=dp[i][m][0]; for(int j=1;j<=m;j++) s[i][j]=s[i][j-1]+dp[i][m][j]; } double p1=1.0,p2=1.0; for(int i=1;i<=t;i++) { p1*=(1-s[i][0]); p2*=(s[i][n-1]-s[i][0]); } printf("%.3f\n",p1-p2); } int main() { while(cin>>m>>t>>n&&m&&t&&n) { for(int i=1;i<=t;i++) for(int j=1;j<=m;j++) cin>>p[i][j]; solve(); } return 0; } [/cpp] 本代码提交AC,用时94MS,内存8228K。]]>

hihoCoder 1052-基因工程

hihoCoder 1052-基因工程 #1052 : 基因工程 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho正在进行一项基因工程实验。他们要修改一段长度为N的DNA序列,使得这段DNA上最前面的K个碱基组成的序列与最后面的K个碱基组成的序列完全一致。 例如对于序列”ATCGATAC”和K=2,可以通过将第二个碱基修改为”C”使得最前面2个碱基与最后面两个碱基都为”AC”。当然还存在其他修改方法,例如将最后一个碱基改为”T”,或者直接将最前面两个和最后面两个碱基都修改为”GG”。 小Hi和小Ho希望知道在所有方法中,修改碱基最少的方法需要修改多少个碱基。 输入 第一行包含一个整数T(1 <= T <= 10),代表测试数据的数量。 每组测试数据包含2行,第一行是一个由”ATCG”4个大写字母组成的长度为N(1 <= N <= 1000)的字符串。第二行是一个整数K(1 <= K <= N)。 输出 对于每组数据输出最少需要修改的碱基数量。 样例输入 2 ATCGATAC 2 ATACGTCT 6 样例输出 1 3


简单的字符串处理题目,该题要求修改字符保证前k个字符和后k个字符相同,求最小的修改次数。这个题目会让人想到字符串的编辑距离,但不完全相同,因为这题是对单个字符串的修改,不能想当然的把前后k个字符取出来,然后逐一比较是否相等,因为如果前k个字符和后k个字符有重叠的话,你修改了某个字符,会影响下一次比较。 比如字符串CTACATGT,长度n=8,k=6,前k个字符串为s1=CTACAT,后k个字符串为s2=ACATGT,我们不能单独求s1和s2的距离,而是需要把这些字符放到原字符串中,再进一步研究你会发现其实就是在原字符串中保证s[i]=s[i+2],其中2=d=n-k。也就是下图中红色和绿色的两组字符要分别相同。 现在我们分析红色那组字符,首先s[0]==s[2]不成立,可以把s[0]改成A;也可以把s[2]改成C,如果我们把s[2]改成C的话,虽然s[0]==s[2]成立,但是s[2]==s[4]又不成立了,又要修改s[4];但是如果我们把s[0]改成A的话,则s[0]==s[2]==s[4]都成立,且修改次数更少。那么要怎样才能使修改的次数最少呢? 我们回到问题的本源:使用红色那组字符都相同,为了使修改次数最少,我们当然是将所有字符统一成当前出现次数最多的那个字符咯,这样只需要修改较少的非同一字符,在这个例子中,就是修改成出现次数最多的A。 如果前k个字符串和后k个字符串没有重叠,那就很简单了,只需依次检查对应字符是否相等。 理解了以上内容,代码很快就能写出来了: [cpp] #include<iostream> #include<string> using namespace std; //求4个数的最大值 int max4(int a,int b,int c,int d) { int maxab=a>b?a:b; int maxcd=c>d?c:d; return maxab>maxcd?maxab:maxcd; } int main() { int t,k,n,d,ans;//t,k为题意;n为字符串长度;d为n-k相隔为d的字符需要相等;ans表示需要修改的碱基数量。 string s; cin>>t; while(t–) { ans=0; cin>>s>>k; n=s.size(); d=n-k; if(2*k<=n)//如果最前k个碱基和最后k个碱基没有重叠的话 { for(int i=0;i<k;i++) if(s[i]!=s[i+d])//只需要依次检查对应字符是否相等即可 ans++; } else//如果有重叠,则另行讨论 { for(int i=0;i<d;i++)//把所有碱基分成d组,需要确保每组都是同一个字符 { int A=0,T=0,G=0,C=0,num=0; for(int j=i;j<n;j+=d)//对于每一组,求出该组中出现次数最多的碱基的个数 { num++; if(s[j]==’A’) A++; else if(s[j]==’T’) T++; else if(s[j]==’G’) G++; else C++; } int maxATGC=max4(A,T,G,C);//该组全部修改成出现次数最多的碱基 int change_num=num-maxATGC;//用该组中所有碱基个数减去出现次数最多的碱基个数,就是需要修改的碱基个数 ans+=change_num; } } cout<<ans<<endl; } return 0; } [/cpp] 本代码提交AC,用时1MS,内存0MB。 ]]>

POJ 1321-棋盘问题

POJ 1321-棋盘问题 棋盘问题 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 23209 Accepted: 11512 Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。 Input 输入含有多组测试数据。 每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n 当为-1 -1时表示输入结束。 随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。 Output 对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C< 2^31)。 Sample Input 2 1 #. .# 4 4 …# ..#. .#.. #… -1 -1 Sample Output 2 1 Source 蔡错@pku


这题可以看成是深度搜索的样题或者经典题。有点类似八皇后问题,题目要求在棋盘上能放棋子的区域放上规定数目的棋子,且保证同行和同列最多放一个棋子,问一共有多少种解法。 本题的限制条件就是同行和同列最多放一个棋子,那么我们在搜索的时候就可以以行为单位搜索,每次测试一行,这样就保证同一行最多放了一个棋子,那么怎样保证同列最多放一个棋子呢?我们可以设置一个数组`int col[]`数组,`col[i]=0`表示该行还没有放棋子;`col[i]=1`表示该行已经放了棋子了。这样我们在搜索的时候就可以这样: [cpp] if(board[i][j]==’#’&&col[j]==0)//如果可以放棋子,且这一列上还没有放过棋子 { col[j]=1;//则在该处放棋子 dfs(i,placed_num+1);//继续往下搜索 col[j]=0;//回溯 } [/cpp] 本题的思路和代码都不难,对于深度搜索的题目,还是要多做,做多了自然会摸索出其中的固定模式。下面是完整代码: [cpp] #include<iostream> using namespace std; int n,k,ans; char board[9][9];//棋盘 int col[9];//每一列是否放置了棋子,0没放;1放了。 //深度搜索,row表示上一次搜索的行号,placed_num表示已经放了几个棋子了 void dfs(int row,int placed_num) { if(placed_num==k)//如果所有棋子都放好了,则找到一个解 { ans++; return; } for(int i=row+1;i<n;i++)//否则的话,从下一行开始搜索每一行 { for(int j=0;j<n;j++)//对该行的所有位置(列) { if(board[i][j]==’#’&&col[j]==0)//如果可以放棋子,且这一列上还没有放过棋子 { col[j]=1;//则在该处放棋子 dfs(i,placed_num+1);//继续往下搜索 col[j]=0;//回溯 } } } } //初始的时候,每一列都没有放棋子 void init_col() { for(int i=0;i<9;i++) col[i]=0; } int main() { while(cin>>n>>k&&n!=-1&&k!=-1) { ans=0; init_col(); for(int i=0;i<n;i++) for(int j=0;j<n;j++) cin>>board[i][j]; dfs(-1,0);//row=-1;placed_num=0 cout<<ans<<endl; } return 0; } [/cpp] 本代码AC,用时32MS,内存216K。]]>