题目来源: openjudge
001:特殊密码锁
描述
有一种特殊的二进制密码锁,由n个相连的按钮组成(n<30),按钮有凹/凸两种状态,用手按按钮会改变其状态。
然而让人头疼的是,当你按一个按钮时,跟它相邻的两个按钮状态也会反转。当然,如果你按的是最左或者最右边的按钮,该按钮只会影响到跟它相邻的一个按钮。
当前密码锁状态已知,需要解决的问题是,你至少需要按多少次按钮,才能将密码锁转变为所期望的目标状态。
输入
两行,给出两个由0、1组成的等长字符串,表示当前/目标密码锁状态,其中0代表凹,1代表凸。
输出
至少需要进行的按按钮操作次数,如果无法实现转变,则输出impossible。
样例输入
011
000
样例输出
1
一个解答示例
按钮按两次会变回原样,所以只需要考虑每个按钮按还是不按就可以。
枚举每个按钮的情况太多,可以分两种,第一个按钮按一下/第一个按钮不按。
然后根据第m-1个按钮判断第m个按钮按不按:比如如果第一个按钮如果和目标一致则第二个按钮不按,不一致则按下第二个按钮来使第一个按钮符合目标。
最后看最后一个按钮是否与目标一致,一致则可行,统计按的次数并输出,最后一个按钮与目标不一致则不可行。如果两种情况都不可行就是imposible。
由于n<30,每一位都是0或1,可以用一个int变量储存,按位操作。
其中__builtin_popcount是求数字二进制中1的个数的函数
#include<iostream>
#include<string>
using namespace std;
int GetBit(int c, int i){return (c>>i)&1;}
void SetBit(int & c, int i, int v){
if(v) c |= (1<<i);
else c &= ~(1<<i);
}
void Flip(int & c, int i){c ^= (1<<i);}
int main(){
int bori=0,ori=0,tag=0,swt=0;
string s;
cin >> s;
int l = s.size();
for(int i=0;i<l;++i){
if(s.at(i) == '1') SetBit(bori, l-i-1, 1);
}
cin >> s;
for(int i=0;i<l;++i){
if(s.at(i) == '1') SetBit(tag, l-i-1, 1);
}
// 第一个按钮按一次
ori = bori;swt=0;
SetBit(swt, 0, 1);
Flip(ori, 0);
Flip(ori, 1);
for(int i=0;i<l-1;++i){
if(GetBit(ori, i) != GetBit(tag, i)){
SetBit(swt, i+1, 1);
//Flip(ori, i);
Flip(ori, i+1);
if(i+2<l) Flip(ori, i+2);
}
}
if(GetBit(ori, l-1) == GetBit(tag, l-1)){
cout << __builtin_popcount(swt) << endl;
return 0;
}
// 一不
ori = bori;swt=0;
for(int i=0;i<l-1;++i){
if(GetBit(ori, i) != GetBit(tag, i)){
SetBit(swt, i+1, 1);
//Flip(ori, i);
Flip(ori, i+1);
if(i+2<l) Flip(ori, i+2);
}
}
if(GetBit(ori, l-1) == GetBit(tag, l-1)){
cout << __builtin_popcount(swt) << endl;
return 0;
}
cout << "impossible" << endl;
return 0;
}
002:拨钟问题
时间限制: 1000ms 内存限制: 65536kB
描述
有9个时钟,排成一个3*3的矩阵。
|-------| |-------| |-------|
| | | | | | |
|---O | |---O | | O |
| | | | | |
|-------| |-------| |-------|
A B C
|-------| |-------| |-------|
| | | | | |
| O | | O | | O |
| | | | | | | | |
|-------| |-------| |-------|
D E F
|-------| |-------| |-------|
| | | | | |
| O | | O---| | O |
| | | | | | | |
|-------| |-------| |-------|
G H I
(图 1)
现在需要用最少的移动,将9个时钟的指针都拨到12点的位置。共允许有9种不同的移动。如下表所示,每个移动会将若干个时钟的指针沿顺时针方向拨动90度。
移动 影响的时钟
1 ABDE
2 ABC
3 BCEF
4 ADG
5 BDEFH
6 CFI
7 DEGH
8 GHI
9 EFHI
输入
9个整数,表示各时钟指针的起始位置,相邻两个整数之间用单个空格隔开。其中,0=12点、1=3点、2=6点、3=9点。
输出
输出一个最短的移动序列,使得9个时钟的指针都指向12点。按照移动的序号从小到大输出结果。相邻两个整数之间用单个空格隔开。
样例输入
3 3 0
2 2 2
2 1 2
样例输出
4 5 8 9
一个参考解答
枚举ABC的转动次数,之后DEFGHI的转动次数分别由保证ABCDGF朝向12来计算,然后判断剩下的EHI三个钟是否指向12,是就输出结束,不是就继续。
参考改进
#include<iostream>
using namespace std;
int main(){
int n[10];
cin >> n[0] >> n[1] >> n[2];
cin >> n[3] >> n[4] >> n[5];
cin >> n[6] >> n[7] >> n[8];
int zh[10];
int nn[10];
for(zh[0]=0;zh[0]<4;++zh[0]){
for(zh[1]=0;zh[1]<4;++zh[1]){
for(zh[2]=0;zh[2]<4;++zh[2]){
zh[3] = (4-(n[0]+zh[0]+zh[1])%4)%4;
zh[4] = (4-(n[1]+zh[0]+zh[1]+zh[2])%4)%4;
zh[5] = (4-(n[2]+zh[1]+zh[2])%4)%4;
zh[6] = (4-(n[3]+zh[0]+zh[3]+zh[4])%4)%4;
zh[8] = (4-(n[5]+zh[2]+zh[4]+zh[5])%4)%4;
//zh[7] = (4-(n[6]+zh[3]+zh[6]+zh[7])%4)%4;
zh[7] = (4-(n[7]+zh[4]+zh[6]+zh[8])%4)%4;
//zh[7] = (4-(n[4]+zh[0]+zh[2]+zh[4]+zh[6]+zh[8])%4)%4;
//int sum = 0;
//sum += n[6]+zh[3]+zh[6]+zh[7];
int sum1 = n[4]+zh[0]+zh[2]+zh[4]+zh[6]+zh[8];
int sum2 = n[6]+zh[3]+zh[6]+zh[7];
int sum3 = n[8]+zh[5]+zh[7]+zh[8];
if(sum1%4==0 && sum2%4==0 && sum3%4==0){
for(int i=0;i<9;++i){
if(zh[i])
for(int j=0;j<zh[i];++j)
printf("%d ", i+1);
}
putchar('\n');
return 0;
}
}
}
}
return -1;
}
003:全排列
总时间限制: 1000ms 内存限制: 65536kB
描述
给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。 我们假设对于小写字母有’a’ < ‘b’ < … < ‘y’ < ‘z’,而且给定的字符串中的字母已经按照从小到大的顺序排列。
输入
输入只有一行,是一个由不同的小写字母组成的字符串,已知字符串的长度在1到6之间。
输出
输出这个字符串的所有排列方式,每行一个排列。要求字母序比较小的排列在前面。字母序如下定义:
已知S = s1s2…sk , T = t1t2…tk,则S < T 等价于,存在p (1 <= p <= k),使得
s1 = t1, s2 = t2, …, s~p - 1~ = t~p - 1~, sp < tp成立。
样例输入
abc
样例输出
abc
acb
bac
bca
cab
cba
一个参考解答
递归
#include<iostream>
#include<string>
using namespace std;
void solve(string f, string s, int n){
if(n==2){
cout << f << s << endl;
cout << f << s.at(1) << s.at(0) << endl;
}else{
for(int i=0;i<n;++i){
solve(f+s.at(i), s.substr(0,i)+s.substr(i+1,n-1), n-1);
}
}
}
int main(){
string s;
cin >> s;
int n=s.size();
if(n<2) cout << s << endl;
else solve("", s, n);
return 0;
}
004:2的幂次方表示
时间限制: 1000ms 内存限制: 65536kB
描述
任何一个正整数都可以用2的幂次方表示。例如:
137=27+23+20
同时约定方次用括号来表示,即ab可表示为a(b)。由此可知,137可表示为:
2(7)+2(3)+2(0)
进一步:7=22+2+20(21用2表示)
3=2+20
所以最后137可表示为:
2(2(2)+2+2(0))+2(2+2(0))+2(0)
又如:
1315=210+28+25+2+1
所以1315最后可表示为:2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
输入
一个正整数n(n≤20000)。
输出
一行,符合约定的n的0,2表示(在表示中不能有空格)。
样例输入
137
样例输出
2(2(2)+2+2(0))+2(2+2(0))+2(0)
来源
NOIP1998复赛 普及组 第一题
一个参考解答
#include<iostream>
#include<bitset>
using namespace std;
void solve(int n){
bitset<16> b(n); // n<=20000=0b0100111000100000
bool start=false;
for(int i=15;i>=0;--i){
if(b[i]==1){
if(start) cout << "+";
else start=true;
if(i==0) cout << "2(0)";
else if(i==1) cout << "2";
else{
cout << "2(";
solve(i);
cout << ")";
}
}
}
}
int main(){
int n;
cin >> n;
solve(n);
putchar('\n');
return 0;
}
005:Boolean Expressions
时间限制: 1000ms 内存限制: 65536kB
描述
The objective of the program you are going to produce is to evaluate boolean expressions as the one shown next:
Expression: ( V | V ) & F & ( F | V )
where V is for True, and F is for False. The expressions may include the following operators: ! for not , & for and, | for or , the use of parenthesis for operations grouping is also allowed.
To perform the evaluation of an expression, it will be considered the priority of the operators, the not having the highest, and the or the lowest. The program must yield V or F , as the result for each expression in the input file.
输入
The expressions are of a variable length, although will never exceed 100 symbols. Symbols may be separated by any number of spaces or no spaces at all, therefore, the total length of an expression, as a number of characters, is unknown.
The number of expressions in the input file is variable and will never be greater than 20. Each expression is presented in a new line, as shown below.
输出
For each test expression, print "Expression " followed by its sequence number, ": ", and the resulting value of the corresponding test expression. Separate the output for consecutive test expressions with a new line.
Use the same format as that shown in the sample output shown below.
样例输入
( V | V ) & F & ( F| V)
!V | V & V & !F & (F | V ) & (!F | F | !V & V)
(F&F|V|!V&!F&!(F|F&V))
样例输出
Expression 1: F
Expression 2: V
Expression 3: V
来源
México and Central America 2004
一个参考解答
题意就是判断一个式子的布尔值,V表示真,F表示假,& | ! 分别表示与或非,还可能有括号。
#include<iostream>
#include<string>
using namespace std;
bool solve(string s){
bool result=false,tp;
bool fei=false;
bool st=false;
int numk=0;
int pos=0;
int n=s.size();
int fl=0;
for(int i=0;i<n;++i){
if(st){
if(s.at(i)==')'){
if(--numk==0){
tp = solve(s.substr(pos+1, i-pos-1));
if(fei){tp = !tp;fei=false;}
if(fl==0) result=tp;
else if(fl==1) result = result&&tp;
else if(fl==2) result =result||tp;
st=false;
}
} else if(s.at(i)=='('){++numk;}
} else {
switch(s.at(i)){
case '(':
pos=i;st=true;numk++;break;
case 'V':
tp=true;if(fei){tp = !tp;fei=false;}
if(fl==0) result=tp;
else if(fl==1) result = result&&tp;
else if(fl==2) result =result||tp;
break;
case 'F':
tp=false;if(fei){tp = !tp;fei=false;}
if(fl==0) result=tp;
else if(fl==1) result = result&&tp;
else if(fl==2) result =result||tp;
break;
case '&':
fl=1;break;
case '|':
fl=2;break;
case '!':
fei = !fei;break;
case ' ':
break;
}
/*if(s.at(i)=='('){st=true;numk++;}
else if(s.at*/
}
//if(result) cout <<s.at(i) << "-V" << endl;
//else cout <<s.at(i) << "-F" << endl;
}
// cout << s << "---" << result << endl;
return result;
}
int main(){
string s;
int i=1;
//string test = "0123456789";
//cout << test.substr(0,3) << "_" << test.substr(1,3) <<endl;
while(getline(cin,s)){
cout << "Expression " << i << ": ";
if(solve(s)) cout << "V" << endl;
else cout << "F" << endl;
++i;
}
return 0;
}
006:简单的整数划分问题
总时间限制: 100ms 内存限制: 65536kB
描述
将正整数n 表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 ,k>=1 。
正整数n 的这种表示称为正整数n 的划分。正整数n 的不同的划分个数称为正整数n 的划分数。
输入
标准的输入包含若干组测试数据。每组测试数据是一个整数N(0 < N <= 50)。
输出
对于每组测试数据,输出N的划分数。
样例输入
5
样例输出
7
提示
5, 4+1, 3+2, 3+1+1, 2+2+1, 2+1+1+1, 1+1+1+1+1
一个参考解答
#include<iostream>
using namespace std;
int solve(int n, int m){
if(n<=0) return 0;
if(n==1||m==1) return 1;
int res=0;
if(m>=n) res++;
for(int i=m;i>1;--i){
res += solve(n-i, i);
}
if(n>1) res++;
return res;
}
int main(){
int n;
while(cin >> n){
cout << solve(n, n) << endl;
}
//cin >> n;
//cout << solve(n, n) << endl;
//for(int i=1;i<=50;++i){
// cout << solve(i,i) << endl;
//}
return 0;
}
007:Aggressive cows
总时间限制: 1000ms 内存限制: 65536kB
描述
Farmer John has built a new long barn, with N (2 <= N <= 100,000) stalls. The stalls are located along a straight line at positions x1,…,xN (0 <= xi <= 1,000,000,000).
His C (2 <= C <= N) cows don’t like this barn layout and become aggressive towards each other once put into a stall. To prevent the cows from hurting each other, FJ want to assign the cows to the stalls, such that the minimum distance between any two of them is as large as possible. What is the largest minimum distance?
输入
-
Line 1: Two space-separated integers: N and C
-
Lines 2…N+1: Line i+1 contains an integer stall location, xi
输出
- Line 1: One integer: the largest minimum distance
样例输入
5 3
1
2
8
4
9
样例输出
3
提示
OUTPUT DETAILS:
FJ can put his 3 cows in the stalls at positions 1, 4 and 8, resulting in a minimum distance of 3.
Huge input data,scanf is recommended.
来源
USACO 2005 February Gold
一个参考解答
大意是把C头牛分开在N个地方,(这N个地方在一条线上),使得牛之间相隔尽可能远,求这种情况下牛之间最近的距离。
二分法尝试(不标准),最小间隔1,最大为最远的地点间距离除以牛的数量减一,逐渐尝试:看某间隔下N个地方能放下多少头牛,能放下超过C头牛该间隔就可行。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
int N,C;
cin >> N >> C;
vector<int> stall;
int num;
for(int i=0;i<N;++i){
cin >> num;
stall.push_back(num);
}
sort(stall.begin(),stall.end());
//for(int n:stall){
// cout << n << " ";
//}
int left=1;
int right=(stall.at(N-1)-stall.at(0))/(C-1);
int n,tag,x=(right-left)/2+left;
while(left+1<right){
x=(right-left)/2+left;
n=1;
tag=stall.at(0)+x;
for(int st:stall){
if(st>=tag){
++n;
tag=st+x;
}
}
if(n>=C){
left=x;
} else {
right=x;
}
}
cout << left << endl;
return 0;
}
008:派
总时间限制: 1000ms 内存限制: 65536kB
描述
我的生日要到了!根据习俗,我需要将一些派分给大家。我有N个不同口味、不同大小的派。有F个朋友会来参加我的派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。
我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。
请问我们每个人拿到的派最大是多少?每个派都是一个高为1,半径不等的圆柱体。
输入
第一行包含两个正整数N和F,1 ≤ N, F ≤ 10 000,表示派的数量和朋友的数量。
第二行包含N个1到10000之间的整数,表示每个派的半径。
输出
输出每个人能得到的最大的派的体积,精确到小数点后三位。
样例输入
3 3
4 3 3
样例输出
25.133
一个参考解答
还是找好两端后二分尝试
#include<iostream>
#include<vector>
#include<algorithm>
#include<math.h>
using namespace std;
int main(){
int N,F;
cin >> N >> F;
vector<int> pie;
int num;
double pi=acos(-1);
double eps=1e-6;
for(int i=0;i<N;++i){
cin >> num;
pie.push_back(num);
}
sort(pie.begin(),pie.end());
double left=0;
double right=pie.at(N-1)*pie.at(N-1);
double x=(right-left)/2+left;
int n;
while(right-left>eps){
x=(right-left)/2+left;
n=0;
for(int r:pie){
double v=r*r;
n += (int)(v/x);
}
if(n>=F+1){
left=x;
} else {
right=x;
}
}
//cout << left*pi << endl;
printf("%.3f\n",left*pi);
return 0;
}
009:月度开销
总时间限制: 1000ms 内存限制: 65536kB
描述
农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。
约翰打算为连续的M (1 ≤ M ≤ N) 个财政周期创建预算案,他把一个财政周期命名为fajo月。每个fajo月包含一天或连续的多天,每天被恰好包含在一个fajo月里。
约翰的目标是合理安排每个fajo月包含的天数,使得开销最多的fajo月的开销尽可能少。
输入
第一行包含两个整数N,M,用单个空格隔开。
接下来N行,每行包含一个1到10000之间的整数,按顺序给出接下来N天里每天的开销。
输出
一个整数,即最大月度开销的最小值。
样例输入
7 5
100
400
300
100
500
101
400
样例输出
500
提示
若约翰将前两天作为一个月,第三、四两天作为一个月,最后三天每天作为一个月,则最大月度开销为500。其他任何分配方案都会比这个值更大。
一个参考解答
#include<iostream>
#include<vector>
//#include<algorithm>
//#include<numeric>
using namespace std;
int main(){
int N,M;
cin >> N >> M;
vector<int> cost;
int num;
int left=0;
int right=0;
for(int i=0;i<N;++i){
cin >> num;
if(num>left) left=num;
right+=num;
cost.push_back(num);
}
//int right=accumulate(cost.begin(),cost.end(),0);
//sort(cost.begin(),cost.end());
int x=(right-left)/2+left;
int res=right;
int n;
int mcost;
//cout << left << "-" << right << endl;
while(right>=left){
x=(right-left)/2+left;
mcost=0;
n=1;
for(int c:cost){
mcost += c;
if(mcost>x){
mcost=c;
++n;
if(n>M)break;
}
}
if(n>M){
left=x+1;
} else {
right=x-1;
res=x;
}
//cout << left << "-" << right << endl;
}
cout << res << endl;
return 0;
}
010:输出前k大的数
总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB
描述
给定一个数组,统计前k大的数并且把这k个数从大到小输出。
输入
第一行包含一个整数n,表示数组的大小。n < 100000。
第二行包含n个整数,表示数组的元素,整数之间以一个空格分开。每个整数的绝对值不超过100000000。
第三行包含一个整数k。k < n。
输出
从大到小输出前k大的数,每个数一行。
样例输入
10
4 5 6 9 8 7 1 2 3 0
5
样例输出
9
8
7
6
5
一个参考解答
注:老师应该是想让用分治(二分归并)自己写个排序算法,试了试用sort函数,然后通过了,,(可以不用vector),分治算法见下一题
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
int N,M;
cin >> N;
vector<int> v;
int n;
for(int i=0;i<N;++i){
cin >> n;
v.push_back(n);
}
sort(v.begin(),v.end());
cin >> M;
for(int i=0;i<M;++i){
cout << v.at(N-1-i) << endl;
}
return 0;
}
011:求排列的逆序数
总时间限制: 1000ms 内存限制: 65536kB
描述
在Internet上的搜索引擎经常需要对信息进行比较,比如可以通过某个人对一些事物的排名来估计他(或她)对各种不同信息的兴趣,从而实现个性化的服务。
对于不同的排名结果可以用逆序来评价它们之间的差异。考虑1,2,…,n的排列i1,i2,…,in,如果其中存在j,k,满足 j < k 且 ij > ik, 那么就称(ij,ik)是这个排列的一个逆序。
一个排列含有逆序的个数称为这个排列的逆序数。例如排列 263451 含有8个逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8。显然,由1,2,…,n 构成的所有n!个排列中,最小的逆序数是0,对应的排列就是1,2,…,n;最大的逆序数是n(n-1)/2,对应的排列就是n,(n-1),…,2,1。逆序数越大的排列与原始排列的差异度就越大。
现给定1,2,…,n的一个排列,求它的逆序数。
输入
第一行是一个整数n,表示该排列有n个数(n <= 100000)。
第二行是n个不同的正整数,之间以空格隔开,表示该排列。
输出
输出该排列的逆序数。
样例输入
6
2 6 3 4 5 1
样例输出
8
提示
1.利用二分归并排序算法(分治);
2.注意结果可能超过int的范围,需要用long long存储。
来源
习题(15-4)
一个参考解答
#include<iostream>
using namespace std;
long long cnt=0;
void merge(int a[], int s, int m, int e, int tmp[]){
int pb=0,p1=s,p2=m+1;
while(p1<=m && p2<=e){
if(a[p1]<a[p2]){ tmp[pb++]=a[p1++];}
else {
tmp[pb++]=a[p2++];
cnt += m-p1+1;
}
}
while(p1<=m) tmp[pb++]=a[p1++];
while(p2<=e) tmp[pb++]=a[p2++];
for(int i=0;i<e-s+1;++i){
a[s+i] = tmp[i];
}
}
void MergeSort(int a[], int s, int e, int tmp[]){
if(s<e){
int m=s+(e-s)/2;
MergeSort(a,s,m,tmp);
MergeSort(a,m+1,e,tmp);
merge(a,s,m,e,tmp);
}
}
int main(){
int a[100001];
int b[100001];
int n;
cin >> n;
for(int i=0;i<n;++i){
scanf("%d", &a[i]);
}
MergeSort(a,0,n-1,b);
//for(int i=0;i<n;++i) printf("%d ",a[i]);
cout << cnt << endl;
return 0;
}
012:拦截导弹
总时间限制: 1000ms 内存限制: 65536kB
描述
某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。
输入
输入有两行,
第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25),
第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。
输出
输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。
样例输入
8
300 207 155 300 299 170 158 65
样例输出
6
来源
医学部计算概论2006期末考试题
一个参考解答
动态规划
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int k;
cin >> k;
int n[26];
int maxdd[26];
for(int i=0;i<k;++i)
{scanf("%d", &n[i]);maxdd[i]=1;}
for(int i=1;i<k;++i){
for(int j=0;j<i;++j){
if(n[i]<=n[j]){
maxdd[i] = max(maxdd[i],maxdd[j]+1);
}
}
}
//for(int i=0;i<k;++i) cout << maxdd[i] << " ";
//cout << endl;
cout << *max_element(maxdd, maxdd+k) << endl;
return 0;
}
013:Zipper
总时间限制: 1000ms 内存限制: 65536kB
描述
Given three strings, you are to determine whether the third string can be formed by combining the characters in the first two strings. The first two strings can be mixed arbitrarily, but each must stay in its original order.
For example, consider forming “tcraete” from “cat” and “tree”:
String A: cat
String B: tree
String C: tcraete
As you can see, we can form the third string by alternating characters from the two strings. As a second example, consider forming “catrtee” from “cat” and “tree”:
String A: cat
String B: tree
String C: catrtee
Finally, notice that it is impossible to form “cttaree” from “cat” and “tree”.
输入
The first line of input contains a single positive integer from 1 through 1000. It represents the number of data sets to follow. The processing for each data set is identical. The data sets appear on the following lines, one data set per line.
For each data set, the line of input consists of three strings, separated by a single space. All strings are composed of upper and lower case letters only. The length of the third string is always the sum of the lengths of the first two strings. The first two strings will have lengths between 1 and 200 characters, inclusive.
输出
For each data set, print:
Data set n: yes
if the third string can be formed from the first two, or
Data set n: no
if it cannot. Of course n should be replaced by the data set number. See the sample output below for an example.
样例输入
3
cat tree tcraete
cat tree catrtee
cat tree cttaree
样例输出
Data set 1: yes
Data set 2: yes
Data set 3: no
来源
Pacific Northwest 2004
一个参考解答
#include<iostream>
#include<string>
using namespace std;
bool solve(string s1, string s2, string s3){
int n1=s1.size(),n2=s2.size(),n3=s3.size();
bool dd[202][202]={false};
dd[0][0]=true;
for(int i=0;i<=n1;++i){
for(int j=0;j<=n2;++j){
if(i>=1 && dd[i-1][j] && s3.at(i+j-1)==s1.at(i-1)) dd[i][j] = true;
if(j>=1 && dd[i][j-1] && s3.at(i+j-1)==s2.at(j-1)) dd[i][j] = true;
}
}
return dd[n1][n2];
}
int main(){
int n;
cin >> n;
string s1,s2,s3;
for(int i=0;i<n;++i){
cin >> s1 >> s2 >> s3;
if(solve(s1,s2,s3)) cout << "Data set " << (i+1) << ": yes" << endl;
else cout << "Data set " << (i+1) << ": no" << endl;
}
return 0;
}