文章目录
零碎知识点整理
isalpha(a)
判断字符a是否为字母,返回值非0表示a是字母,否则不是字母。isdigit(a)
判断字符a是否为数字。isupper(a)
判断字符a是否为大写英文字母。islower(a)
判断字符a是否为小写英文字母。toupper(a)
将字符a转换为大写字母,tolower(a)
将字符a转换为小写字母。注意头文件为<cctype>或<ctype.h>。
- bool类型的返回值默认为true,即没有返回return 时,默认返回true。
- printf输出string类型的字符串时,需要采用这种方式
printf("%s\n", b[i].c_str());
。
题目
340 - Master-Mind Hints(数组统计)
题目链接:340 - Master-Mind Hints
参考博文:340 - Master-Mind Hints
- 题目大意:题意过于复杂。大概如下,先输入一个n,为序列长度。然后是若干行数据,第一行是答案序列,接下来的若干行是猜测序列,以一组都为0的数位结尾。此为一组数据。所有数据的结尾,会有一个“0”做为标记。找到答案序列和猜测序列之间,两序列相同位置拥有相等数字的组数A,并找到两序列都拥有该数字B,但位置不对应的组数。输出是用格式为(A,B)。
- 思路:重点是找出A后,根据A找出B。找B时需要寻找储存答案序列和猜测序列各个数值的个数,然后根据取二者值最小者,累加后,在减去A即可得到B。避免了搜索的复杂运算。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX = 1005;
int N, a[MAX], b[MAX], n1[15], n2[15];
int main()
{
int T = 1;
while(scanf("%d", &N)!=EOF && N)
{
memset(n1, 0, sizeof(n1));
for(int i=0; i<N; i++)
{
scanf("%d", &b[i]);
n1[b[i]]++;
}
printf("Game %d:\n", T++);
while(1)
{
int A = 0, B = 0;
memset(n2, 0, sizeof(n2));
for(int i=0; i<N; i++)
{
scanf("%d", &a[i]);
n2[a[i]]++;
if(b[i]==a[i]) A++;
}
if(!a[0]) break;
for(int i=1; i<10; i++)
{
B += min(n1[i], n2[i]);
}
B -= A;
printf(" (%d,%d)\n", A, B);
}
}
return 0;
}
1583 - Digit Generator(预处理,查表)
- 题目大意:输入一个数,找出这个数的最小生成元。
生成元的意思是:一个整数,比如245,它本身加上它各位数之和等于256 (= 245 + 2 + 4 + 5),则245就是256的一个生成元。 - 思路:有两种方法,一个是打表,即遍历出0-100000范围内的所有生成元,并将其与对应的数建立映射,则在输入该数时,直接可得该数的生成元。第二种方法是枚举,但是对于数字d,可以只枚举max(0, d-50)-d范围内的数字(从生成元的定义和范围可知)。
代码:
打表法:
参考博文:1583 - Digit Generator
枚举法:
#include <iostream>
#include <cstring>
using namespace std;
int T, d;
bool check(int n, int t)
{
int a = n, b = n;
while(b)
{
a += b%10;
b /= 10;
}
if(a==t) return 1;
else return 0;
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &d);
int flag = 0;
int st = max(0, d-50);
for(int i=st; i<d; i++)
{
if(check(i, d))
{
flag = i;
break;
}
}
if(!flag) printf("0\n");
else printf("%d\n", flag);
}
return 0;
}
1584 - Circular Sequence(字典序)
- 题目大意:输入一个长度为n(n≤100)的环状DNA串(只包含A、C、G、T这4种字符)的一种表示法,你的任务是输出该环状串的最小字典序表示。例如,CTCC的最小表示是CCCT,CGAGTCAGCT的最小表示为AGCTCGAGTC。
- 思路:遍历字符串中所有的字符,以该字符为起始,然后比较得出最小的字典序开始的字符下标。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
int len, T;
char s[101];
//比较以p开始的字符串和q开始的字符串那个字典序小
int Less(char s[], int p, int q)
{
for(int i=0; i<len; i++)
{
if(s[(p+i)%len]!=s[(q+i)%len])
return s[(p+i)%len]<s[(q+i)%len];
}
return 0;//相等
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%s", s);
int ans = 0;
len = strlen(s);
for(int i=1; i<len; i++)
{
if(Less(s, i, ans)) ans = i;
}
for(int i=0; i<len; i++)
{
printf("%c", s[(i+ans)%len]);
}
printf("\n");
}
return 0;
}
455 - Periodic Strings
- 题目大意:给出一个字符串,其是由一个子串重复1次或多次组成的,求出最小周期子串的长度。
- 思路:可以将周期从1到s.size()遍历,判断该周期是否存在周期子串。
代码:
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
const int MAX = 100;
int T;
char s[MAX];
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%s", s);
int len = strlen(s);
for(int i=1; i<=len; i++)
{
if(len%i) continue;//周期是否可整除
int cnt = i;
char a[MAX], b[MAX];
strncpy(a, s, i);//获取该周期的第一个子串
a[i] = '\0';//注意需要添加结束字符,否则C语言不认为其结束
//遍历判断该子串是否是周期子串
while(cnt<len)
{
strncpy(b, s+cnt, i);
b[i] = '\0';
if(strcmp(a, b)!=0)//与第一个子串不同
break;
cnt += i;
}
if(cnt==len)
{
printf("%d\n", i);
break;
}
}
if(T) printf("\n");
}
return 0;
}
227 - Puzzle
题目链接:227 - Puzzle
- 题目大意:给出一个5*5的网格,网格中只有一个空格。然后给出一些移动指令ABLR(上下左右)来一定空格,输出执行指令之后的网格。
- 思路:模拟题目。按照指令将空格与相邻的字符交换即可,还需要判断是否出界。重点是输入问题,空格在一行的最后,可能会变成回车(即空格消失),需要再恢复一下。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
char G[10][10], ch;
int main()
{
int sx, sy, T = 1;
while(1)
{
//注意fgets读入了回车
fgets(G[0], 10, stdin);
if(strlen(G[0])<5 && G[0][0]=='Z') break;
for(int i=1; i<5; i++)
{
fgets(G[i], 10, stdin);
}
for(int i=0; i<5; i++)
{
for(int j=0; j<5; j++)
{
if(G[i][j]==' ' || G[i][j]=='\n')//空格在一行的最后,可能变成了换行,需要恢复一下
{
G[i][j] = ' ';//恢复
sx = i, sy = j;//获得空格起始坐标
}
}
}
//根据指令模拟
int flag = 1;
while(ch=getchar())
{
if(ch=='0') break;
if(!flag) continue;
if(ch=='A')
{
if(sx-1<0)
{
flag = 0;
continue;
}
swap(G[sx][sy], G[sx-1][sy]);
sx--;
}else if(ch=='B')
{
if(sx+1>=5)
{
flag = 0;
continue;
}
swap(G[sx][sy], G[sx+1][sy]);
sx++;
}else if(ch=='L')
{
if(sy-1<0)
{
flag = 0;
continue;
}
swap(G[sx][sy], G[sx][sy-1]);
sy--;
}else if(ch=='R')
{
if(sy+1>=5)
{
flag = 0;
continue;
}
swap(G[sx][sy], G[sx][sy+1]);
sy++;
}
}
getchar();
//输出格式
if(T!=1) printf("\n");
printf("Puzzle #%d:\n", T++);
if(!flag)//存在违法指令
{
printf("This puzzle has no final configuration.\n");
continue;
}
for(int i=0; i<5; i++)
{
for(int j=0; j<5; j++)
{
if(j!=0) printf(" ");
printf("%c", G[i][j]);
}
printf("\n");
}
}
return 0;
}
232 - Crossword Answers
- 题目大意:题目描述过于复杂。在c x r的网格中,有白格或黑格。每个白格中填字母。其中一个格的左或上边 没有格或有黑格,则这样的格为起始格。此外,一个词为从一个起始格 到一个黑格之前的词 或 到网格行列末尾处(across为一行内的词,down为一列内的词)。输入中,黑格用*表示。输入r、c(行、列),当r=0时结束输入测试样例。输出Across和down的词。
- 思路:一个比较好的模拟题目,特点是输出格式考虑的比较多。我为了简化代码,采用了string类型来拼接字符串(直接加就行),采用先存储再输出的方式,具体方法参考代码。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int r, c;
char G[15][15];
string a[101], b[101];
int ida[101], idb[101], id[11][11];
//判断是否是起始点
bool check_id(int x, int y)
{
if(G[x][y]=='*') return 0;
if(x-1<0 || y-1<0) return 1;
if(G[x-1][y]=='*' || G[x][y-1]=='*') return 1;
return 0;
}
int main()
{
int T = 1;
while(scanf("%d", &r)!=EOF && r)
{
//初始化所有元素为空,防止判断错误
fill(a, a+101, "");
fill(b, b+101, "");
scanf("%d", &c);
getchar();
//输入
for(int i=0; i<r; i++)
{
fgets(G[i], 12, stdin);
}
//遍历得到起始点编号
int cnt = 1;
for(int i=0; i<r; i++)
{
for(int j=0; j<c; j++)
{
if(check_id(i, j))
{
id[i][j] = cnt++;
}
}
}
//得到Across的字符串
int cnta = 0;
a[cnta] = "";
for(int i=0; i<r; i++)
{
for(int j=0; j<c; j++)
{
if(G[i][j]!='*')//遇到非*,累加字符串
{
a[cnta] += G[i][j];
if(j-1<0 || G[i][j-1]=='*')//得到起始点,则取出其标号
ida[cnta] = id[i][j];
}
else if(a[cnta]!="")//遇到*开始取下一个字符串
cnta++;//a[++cnta] = "";
}
if(a[cnta]!="")
{
cnta++;
//a[++cnta] = "";//换行,取下一个字符串
}
}
//得到Down的字符串
int cntb = 0;
b[cntb] = "";
for(int i=0; i<r; i++)
{
for(int j=0; j<c; j++)
{
if(G[i][j]!='*')//遇到起始点
{
int t = i;
idb[cntb] = id[i][j];//取编号
while(G[t][j]!='*' && t<r)//向下遍历取字符串
{
b[cntb] += G[t][j];
G[t++][j] = '*';
}
cntb++;
//b[++cntb] = "";//开始取下一个字符串
}
}
}
//输出格式
if(T!=1) printf("\n");
printf("puzzle #%d:\n", T++);
printf("Across\n");
for(int i=0; i<cnta; i++)
{
printf("%3d.", ida[i]);//注意三个宽度
printf("%s\n", a[i].c_str());
}
printf("Down\n");
for(int i=0; i<cntb; i++)
{
printf("%3d.", idb[i]);//注意三个宽度
printf("%s\n", b[i].c_str());
}
}
return 0;
}
1368 - DNA Consensus String
题目链接:1368 - DNA Consensus String
- 题目大意:给定m个长度为n的DNA序列,求一个DNA序列,使其到所有这些序列的总hamming距离尽量小,如果有多个解,输出字典顺序的最小解。(hamming距离是两个序列不同字符的个数)
- 思路:用数组统计各列各类字符的个数,然后取每一列出现次数最多的字符,拼接成即可,如果次数相同则ASC码小的优先。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
const int MAX = 1005;
map<char, int>M;
string ans, t="ACGT";
int a[MAX][5];
char s[MAX];
int n, m, len, T, num;
//按字典序赋值
void Init()
{
M['A'] = 0, M['C'] = 1;
M['G'] = 2, M['T'] = 3;
}
int main()
{
Init();
scanf("%d", &T);
while(T--)
{
memset(a, 0, sizeof(a));
scanf("%d%d", &m, &n);
getchar();
//输入并统计次数
for(int k=0; k<m; k++)
{
fgets(s, MAX, stdin);
len = strlen(s)-1;
for(int i=0; i<n; i++)
a[i][M[s[i]]]++;
}
//筛选出每列次数最多字符,并拼接
ans = "", num = 0;
for(int i=0; i<n; i++)
{
int MAX = a[i][0];
char cnt = t[0];
for(int j=1; j<4; j++)
{
if(MAX<a[i][j])
{
MAX = a[i][j];
cnt = t[j];
}
}
ans += cnt;
num += m-MAX;//累加求Ham距离
}
printf("%s\n", ans.c_str());//转成char数组
printf("%d\n", num);
}
return 0;
}
202 - Repeating Decimals
- 题目大意:给出整数a和b,求a除以b得到的小数的循环节,并输出循环节长度。
- 思路:模拟手动除法,但是要将商和余数存储起来,并能够对应起来。当除得的余数与之前的余数重复时,可以断定出现了循环,且该余数对应的上一个下标与商的循环开始下标存在对应关系。然后就是输出的问题。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a, b, c, ci, d, p;
int modc[10000];
int Find(int modc[], int c, int n)
{
for(int i=0; i<n; i++)
{
if(c==modc[i])
{
return i;
}
}
return -1;
}
int main()
{
while(scanf("%d%d", &a, &b)!=EOF)
{
string frac = "";//小数位
memset(modc, 0, sizeof(modc));
ci = a/b, d = a%b;
int p, cnt = 0;//p是循环节开始时的小数下标
modc[cnt++] = d;//记录余数,从获得第一位小数的余数开始记录
while(1)
{
if(d==0)//当可以除尽时
{
frac += '0';
p = frac.size()-1;
break;
}
c = d*10/b;
d = (d*10)%b;
frac += c+'0';
p = Find(modc, d, cnt);//找到重复的余数的下标,该下标对应着小数的下标
if(p!=-1) break;
modc[cnt++] = d;
}
printf("%d/%d = %d.", a, b, ci);
int n = frac.size();
for(int i=0; i<n; i++)
{
if(i==50)//超出50位
{
printf("...");
break;
}
if(i==p) printf("(");//开始循环
printf("%c", frac[i]);
}
printf(")\n");
int r = n-p;//循环节长度
printf(" %d = number of digits in repeating cycle\n\n", r);
}
return 0;
}
10340 - All in All
题目链接:10340 - All in All
- 题目大意:输入a和b两个字符串,b通过删除其中的一些字符能够得到a。
- 思路:直接遍历。因为a中字符需要在b中的顺序不再改变,所以简单的O(n^2)遍历。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX = 100005;
char s1[MAX+1], s2[MAX+1];
int main()
{
while(scanf("%s%s", s1, s2)!=EOF)
{
int len1 = strlen(s1), len2 = strlen(s2), cnt = 0, i, j;
for(i=0; i<len1; i++)
{
for(j=cnt; j<len2; j++)
{
if(s1[i]==s2[j])//相等
{
cnt = j+1;//记录下一次s2开始遍历的下标
break;
}
}
if(j==len2) break;//s2已经遍历完了,但s1还没有
}
if(i==len1)//s1所有字符均在s2中按序排列
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
1587 - Box
题目链接:1587 - Box
- 题目大意:给出6个长方形的长和宽,判断是否能够组成长方体。
- 思路:6个长方形需要满足ab,ac,和bc的关系,且这三种关系,每一种有且仅有两个。具体技巧是排序,方便操作。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
pair<int, int>u[10];
int main()
{
while(scanf("%d%d", &u[0].first, &u[0].second)!=EOF)
{
if(u[0].first>u[0].second) swap(u[0].first, u[0].second);//统一first不能大于second
for(int i=1; i<6; i++)
{
scanf("%d%d", &u[i].first, &u[i].second);
if(u[i].first>u[i].second) swap(u[i].first, u[i].second);
}
sort(u, u+6);
int flag =1;
for(int i=0; i<6; i+=2)//判断只存在三种关系
{
if(u[i].first!=u[i+1].first || u[i].second!=u[i+1].second)
{
flag = 0;
break;
}
}
//判断是否这三种关系是ab,ac,bc
if(u[0].first!=u[2].first || u[0].second!=u[4].first || u[2].second!=u[4].second)
flag = 0;
if(flag)
printf("POSSIBLE\n");
else
printf("IMPOSSIBLE\n");
}
return 0;
}
1588 - Kickdown
题目链接:1588 - Kickdown
- 题目大意:给出两个长度分别为n1,n2且每列高度只为1或2的长条,需要将它们放入一个高度为3的容器,求出能够容纳他们的最短容器长度。
- 思路:本题一遍写过,还没有调试,开心。我的思想是按照两个长条的重合度来求最小长度。假设n2小,重合度范围为[0, n2],且需要从大到小遍历来判断该重合度下是否可以使得两个长条装入容器,如果成功,则直接输出结果,否则继续遍历。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
string s1, s2;
int len1, len2, Max;
void solve(string s1, string s2)
{
Max = len1+len2;
//完全重合的情况,即Max等于最大的一个序列的长度
for(int i=0; i<=len1-len2; i++)
{
int j;
for(j=i; j<i+len2; j++)
{
if(s2[j-i]+s1[j]-'0'-'0'>3)
break;
}
if(j==i+len2)
{
Max = len1;
break;
}
}
if(Max==len1)
{
printf("%d\n", Max);
return;
}
//重合度从大到小遍历,得到最大重合度,则Max=len1+len2-i
for(int i=len2-1; i>=1; i--)
{
int j;
for(j=len2-i; j<len2; j++)
{
if(s2[j]+s1[j-len2+i]-'0'-'0'>3)
break;
}
if(j==len2)
{
Max -= i;
break;
}
for(j=i-1; j>=0; j--)
{
if(s2[j]+s1[len1-i+j]-'0'-'0'>3)
break;
}
if(j==-1)
{
Max -= i;
break;
}
}
printf("%d\n", Max);
}
int main()
{
while(getline(cin, s1))
{
getline(cin, s2);
//确保第二个一定最小
if(s1.size()<s2.size()) swap(s1, s2);
len1 = s1.size(), len2 = s2.size();
solve(s1, s2);
}
return 0;
}
11809 - Floating-Point Numbers
题目链接:11809 - Floating-Point Numbers
参考博文:UVa 11809 Floating-Point Numbers(浮点数)
参考博文:UVA - 11809(Floating-Point Numbers)
- 题目大意:给你浮一个点数表示的最大范围,求出尾数和指数的位数。
- 思路 :对于位数的位数M和指数的位数E,输入的十进制AeB中A和B。可知
- m = 1 − 2 − ( 1 + M ) , e = 2 E − 1 m=1-2^{-(1+M)} ,e=2^E-1 m=1−2−(1+M),e=2E−1, 并可得等式 m ∗ 2 e = A ∗ 1 0 B m*2^e=A*10^B m∗2e=A∗10B,两边同时取对数 l o g 10 m + e ∗ l o g 10 2 = l o g 10 A + B log_{10}m+e*log_{10}2=log_{10}A+B log10m+e∗log102=log10A+B,令 t = l o g 10 m + e ∗ l o g 10 2 t=log_{10}m+e*log_{10}2 t=log10m+e∗log102,则 B = t − l o g 10 A B=t-log_{10}A B=t−log10A,因为1<=A<10,所以 l o g 10 A < 1 log_{10}A<1 log10A<1,因为B表示指数,为正整数,所以B=t/1(向下取整), A = 1 0 t − B A=10^{t-B} A=10t−B。
- 所以我们可以采用打表的策略,因为我们有M和E的取值范围 9 ≥ M ≥ 0 和30 ≥ E ≥ 1,所以可以得到通过M和E求出相应的A和B(使用二维数组),然后输入A和B时与表中的元素比较可得出其对应标号的M和E,但要注意精度大概1e-5即可。
代码:
#include <stdio.h>
#include <string.h>
#include <math.h>
#define max1 15
#define max2 35
#define min_diff 1e-5//设置误差
double M[max1][max2];//存储对应尾数
long long E[max1][max2];//存对应指数
void solve(double m, long long e)
{
int flag = 0;
for(int i=0; i<10; i++)
{
for(int j=1; j<=30; j++)
{
if(e==E[i][j] && fabs(m-M[i][j])<min_diff)
{
flag = 1;
printf("%d %d\n", i, j);
break;
}
}
if(flag)
break;
}
}
int main()
{
double m, t;
long long e;
char s[500];
for(int i=0; i<10; i++)
{
//完全按照公式打表
for(int j=1; j<=30; j++)
{
e = (1<<j)-1;
m = 1-1.0/(1<<(i+1));
t = log10(m) + e*log10(2);
E[i][j] = t/1;
M[i][j] = pow(10,t-E[i][j]);
}
}
while(scanf("%s", s)==1 && strcmp(s,"0e0"))
{
*(strchr(s,'e'))=' '; //将字符串中的e替换成空格
sscanf(s,"%lf %lld", &m, &e);
solve(m,e);
}
return 0;
}