A Great Alchemist
来源:互联网 发布:办公考勤软件 编辑:程序博客网 时间:2024/05/22 17:30
A Great Alchemist
Time limit : 2sec / Stack limit : 256MB / Memory limit : 256MB
Problem
Carol is a great alchemist.
In her world, each metal has a name of 2N (N is an integer) letters long, which consists of uppercase alphabets.
Carol can create metal S3 from S1 and S2 alchemical when she can make the name of S3 by taking N letters each from S1 and S2then rearranging them properly.
You are given 3 names of the metal S1, S2, S3. Determine wether Carol can create S3 from S1 and S2 or not.
Input
The input will be given in the following format from the Standard Input.
S1S2S3
- On the first line, you will be given the name of the first metal material S1.
- On the second line, you will be given the name of the second metal material S2.
- On the third line, you will be given the name of the metal S3, which Carol wants to create.
- Each character in the S1, S2, and S3 will be an uppercase English alphabet letter.
- Each string S1, S2 and S3 has same number of letters and the number is always even.
- It is guaranteed that 2≦|S1|≦105
Output
If Carol can create S3 from S1 and S2, output YES
, if not, output NO
in one line. Make sure to insert a line break at the end of the output.
Input Example 1
- AABCCD
- ABEDDA
- EDDAAA
Output Example 1
- YES
You can make EDDAAA
by picking AAD
from the first metal, and AED
from the second metal.
Input Example 2
- AAAAAB
- CCCCCB
- AAABCB
Output Example 2
- NO
To make AAABCB
, you have to take at least four letters from the first material. So this can't be created alchemical.
这题为实验室阿呆师弟在线笔试的一道题目。
1. 最初拿到题目的时候,由于时间有限,用最直接的暴力方法求解,递归过程中剪枝,代码如下
#include<iostream>#include<cstring>#include<string>using namespace std;bool get(int* cnt1, int* cnt2,int* cnt3,int index,int c1,int c2,int N){ if(index==26) return c1==c2 && c1==N; if(c1>N||c2>N) return false; if(cnt1[index]+cnt2[index]<cnt3[index]) return false; if(cnt1[index]+cnt2[index]==cnt3[index]) return get(cnt1,cnt2,cnt3,index+1,c1+cnt1[index],c2+cnt2[index],N); for(int i = 0; i <= cnt1[index] && cnt3[index]>=i;i++) { if(c1+i>N) return false; if(cnt3[index]-i > cnt2[index]) continue; if(get(cnt1,cnt2,cnt3,index+1,c1+i,c2+cnt3[index]-i,N)) return true; } return false;}int main(int argc, char* argv[]){ char s1[256]; char s2[256]; char s3[256]; cin>>s1; cin>>s2; cin>>s3; int N = strlen(s1)/2; int cnt1[26]; int cnt2[26]; int cnt3[26]; for(int i = 0; i < 26; i++) { cnt1[i] = cnt2[i] = cnt3[i] = 0; } for(int i = 0; i < strlen(s1);i++) { cnt1[s1[i]-'A']++; cnt2[s2[i]-'A']++; cnt3[s3[i]-'A']++; } if(get(cnt1,cnt2,cnt3,0,0,0,N)) { cout<<"YES"<<endl; } else cout<<"NO"<<endl; return 0;}
暴力方法粗暴,但是直观,提交后Time Limit Exceeded.
2. 进一步分析发现在暴力递归过程中会有一些已经计算过的状态再次计算,尝试加入历史记录,增加剪枝条件。
#include<iostream>#include<cstring>#include<string>using namespace std;bool get(int* cnt1, int* cnt2,int* cnt3,int index,int c1,int c2,int N,bool** his){ if(index==26) return c1==c2 && c1==N; if(c1>N||c2>N) return false; if(his[index][c1]==false) return false; if(cnt1[index]+cnt2[index]==cnt3[index]) { his[index][c1] = get(cnt1,cnt2,cnt3,index+1,c1+cnt1[index],c2+cnt2[index],N,his); return his[index][c1]; } for(int i = 0; i <= cnt1[index] && cnt3[index]>=i;i++) { if(c1+i>N) return false; if(cnt3[index]-i > cnt2[index]) continue; if(get(cnt1,cnt2,cnt3,index+1,c1+i,c2+cnt3[index]-i,N,his)) return true; } his[index][c1] = false; return false;}int main(int argc, char* argv[]){ char* s1 = new char[102400]; char* s2 = new char[102400]; char* s3 = new char[102400]; cin>>s1; cin>>s2; cin>>s3; int N = strlen(s1)/2; int cnt1[26]; int cnt2[26]; int cnt3[26]; bool* his[26]; for(int i = 0; i < 26; i++) { cnt1[i] = cnt2[i] = cnt3[i] = 0; } for(int i = 0; i < strlen(s1);i++) { cnt1[s1[i]-'A']++; cnt2[s2[i]-'A']++; cnt3[s3[i]-'A']++; } for(int i = 0; i < 26; i++) { if(cnt1[i] + cnt2[i] < cnt3[i]) { cout<<"NO"<<endl; return false; } } for(int i = 0; i < 26;i++) { his[i] = new bool[N+1]; for(int j = 0; j < N+1; j++) { his[i][j] = true; } } if(get(cnt1,cnt2,cnt3,0,0,0,N,his)) { cout<<"YES"<<endl; } else cout<<"NO"<<endl; return 0;}
提交后大部分测试用例通过,小部分Time Limit Exceeded. 代码应该还有可优化的空间,不过这时候答题时间已经过了。
3. 阿呆说可以用dp求解,不过直观上感觉这道题应该不是dp的路子。重新分析题目后,发现在组合第三个字符串的时候,如果先尽量从第一个字符串里面取字符的话,这时候第一个字符串使用的字符数total1会大于N,这时候如果可以尝试把total1调节到N,那么就可以输出YES,否则就是NO。
这里在调节的时候有一个trick,就是字符A-Z中,无论调节哪一个字符对最终结果都没有影响。为了理解这个过程,我们把字符串组合问题抽象成两个数组A[26]和B[26],分别表示第一个字符串和第二个字符串所使用的字符数。我们希望sum(A)==N并且sum(B)==N,A数组中任意一个element-1,所对应的B数组中的element+1,我们希望通过不断的减少A数组中某些元素的取值,而使得sum(A)==N。这里比较方便的一点就是A数组中的每一个元素在数值减少的时候,可以减少1、2、3...所有合理的数组(是一个连续的过程)。
根据这个思路,可以得到如下代码:
#include<iostream>#include<cstring>#include<string>using namespace std;int main(int argc, char* argv[]){ char* s1 = new char[102400]; char* s2 = new char[102400]; char* s3 = new char[102400]; cin>>s1; cin>>s2; cin>>s3; int N = strlen(s1)/2; int cnt1[26]; int cnt2[26]; int cnt3[26]; int use1[26]; int use2[26]; bool* his[26]; for(int i = 0; i < 26; i++) { cnt1[i] = cnt2[i] = cnt3[i] = 0; use1[i] = use2[i] = 0; } for(int i = 0; i < strlen(s1);i++) { cnt1[s1[i]-'A']++; cnt2[s2[i]-'A']++; cnt3[s3[i]-'A']++; } int total1 = 0; for(int i = 0; i < 26; i++) { if(cnt1[i] + cnt2[i] < cnt3[i]){cout<<"NO"<<endl; return 0;} if(cnt1[i] >= cnt3[i]) use1[i] = cnt3[i]; else use1[i] = cnt1[i]; total1 += use1[i]; use2[i] = cnt3[i] - use1[i]; } if(total1<N){ cout<<"NO"<<endl; return 0;} bool bminus = true; for(int i = 0; i < 26; i++) { while(total1> N && use1[i] > 0 && use2[i] < cnt2[i]) { use1[i]--; use2[i]++; total1--; } if(total1==N) { cout<<"YES"<<endl; return 0; } } if(total1==N) { cout<<"YES"<<endl; } else cout<<"NO"<<endl; return 0;}
这是修改后的最终版代码,提交后AC。
4. 随着这个思路,再进一步分析会发现,total1的数值在调整的过程中是一个线性减小的过程,如果我们再计算一个total2,表示尽可能少的从第一个字符串取字符的情况下,字符串1所使用的字符数,那么如果total1 > N > total2 或者total1== N || total2==N,那么total1就必然能够调整到N,也就可以得到结果。所以可以得到如下更精简的代码:
#include<iostream>#include<cstring>#include<string>using namespace std;int main(int argc, char* argv[]){char* s1 = new char[102400];char* s2 = new char[102400];char* s3 = new char[102400];cin>>s1;cin>>s2;cin>>s3;int N = strlen(s1)/2;int cnt1[26];int cnt2[26];int cnt3[26];int use1,use2;for(int i = 0; i < 26; i++){cnt1[i] = cnt2[i] = cnt3[i] = 0;}for(int i = 0; i < strlen(s1);i++){cnt1[s1[i]-'A']++;cnt2[s2[i]-'A']++;cnt3[s3[i]-'A']++;}int total1 = 0, total2=0;for(int i = 0; i < 26; i++){if(cnt1[i] + cnt2[i] < cnt3[i]){cout<<"NO"<<endl;return false;}if(cnt1[i] >= cnt3[i])total1 += cnt3[i];elsetotal1 += cnt1[i]; //calculate total2if(cnt2[i] >= cnt3[i])total2 += 0;elsetotal2 += cnt3[i]-cnt2[i];}if(total1 > N && total2 < N)cout<<"YES"<<endl;else if(total1 == N || total2 == N)cout<<"YES"<<endl;else cout<<"NO"<<endl;return 0;}提交后AC。
- C - A Great Alchemist
- A Great Alchemist
- A Great Alchemist
- atcoder之A Great Alchemist
- A great day
- A Great Writer
- Building A Great API
- TexturePacker--A great tool!
- A great spectacle
- A Great Start!
- A Great Man:Kalman
- 34. A great talker is a great liar.说大话者多慌言
- What makes a great team
- What Makes A Great Programmer?
- What Makes A Great Programmer?
- To be a great programmer
- codeforces:A. The Great Game
- It is a great start
- 2014秋C++第10周项目6参考-贪财的富翁
- java.io.IOException: Too many open files
- 2014秋C++ 第11周项目 函数及其应用
- boost.Multi_Index库的使用
- NSNotificationCenter 用法详解
- A Great Alchemist
- 高效的SQLSERVER分页查询
- excel函数使用
- 【cocos2dx 3.3】遮罩层的原始实现
- 线性筛法
- nyoj-891
- CentOS,清理缓存(drop_caches)
- 动态规划分析总结——如何设计和实现动态规划算法
- Sqlserver中char,nchar,varchar与Nvarchar的区别分析