5.1 几个简单的题目(有些思想)
致你:
代码只有自己真正写过一遍,才能更好的深入理解它。你看会了,终究是看会了。你会写么?在此过程中出现了什么问题?你在哪里又犯下了什么过错?
WERTYU
我:对于这道题,你如果让我做。我会把各个字符对应的下一个字符以一匹配。然后比较字符相等,逐个比较转换输出。
但
刘:给出的思路,将对应关系的字符都存入一个数组。想要输出,则进行加减运算。
TeX括号
刘:判断左右括号,利用标志进行转换。每次转为时:
q=!q;
这样的标志改变的思路。
周期串
我:把这个字符串分成若干组,组划分从仅1组到n组,以第一组为源头,分别比较各组元素是否全与第一组元素相同,若相同,则把最小即为当前划分组。比较完从1组到n组后,输出最小的那个。
刘:从小到大枚举各个周期。若一个周期满足,则立刻输出该周期,该周期绝对是最小周期。
#include<stdio.h>
int main()
{
char s[80];
scanf("%s", s);
int i = 1;
int j = 1;
int n=strlen(s);
for (int i = 1; i <= n / 2; i++)
{
if (n % i == 0)
{
int ok = 1;
for (int j = i; j < n; j++) //从i之后开始,也就是把i之前的子串作为底板
{
if (s[j] != s[j % i]) //之后的子串一一与底板匹配,若有一个不同则break
{
ok = 0;
break;
}
}
if (ok) //只要一出现第一个子串匹配。那么就立刻输出。并跳出
{
printf("%d", i);
break;
}
}
}
return 0;
}
取最大,最小之类的:那么就是从左,从右开始,并且只要遇到的第一个,就直接跳出。那么即为最大或最小
高精度运算
(用数组来存储整数,模拟手算进行四则运算)
小学生运算
对于int,上限是20亿,可以保存所有的9位数
阶乘的精确值
利用数组来存储大数据。
f[0]保存个位
f[1]保存十位
即,逆序保存(因为如果按照从高到低的顺序存储,那么一旦进位的话,就要移动整个数组了)
并且,在输出的时候,要忽略前导0
#include<stdio.h>
#include<string.h>
#define MAXN 3000
int f[MAXN];
int main()
{
int i, j, n;
scanf("%d", &n);
memset(f, 0, sizeof(f));
f[0] = 1;
for (i = 2; i <=n; i++)
{
int c = 0;
for (j = 0; j < MAXN; j++)
{
/*不能
f[j] = (f[j] * i + c)%10; 如果改变了f[j]那么下一个c就改变了。不是想要的了
c = (f[j] * i + c) / 10;
*/
int s = f[j] * i + c;
f[j] = s % 10;
c = s / 10;
}
}
for (j = MAXN-1; j >= 0; j--) //跳过前导0的思想,只要遇到第一个非0数,就跳出
if (f[j])
break;
for (i = j; i >= 0; i--)
printf("%d", f[i]);
printf("\n");
return 0;
}
跳过前导0的思想,我很喜欢
5.2 高精度运算类bign和重载bign的运算符
//在使用string的时候,要加#include<string> 和 using namespace std;
#include<string>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1000;
struct bign //c++中不需要typedef就可以直接用结构体名定义变量,而且还提供"自动初始化"的功能
{
//要表示1234,则len=4,s[0]=4,s[1]=3,s[2]=2,s[3]=1;
int len, s[maxn]; //len表示位数,s数组是具体的各个数字
bign() //构造函数,作用是进行初始化
{
memset(s, 0, sizeof(s));
len = 1;
}
/*
可以用x="123456"给x赋值了
会将字符串转化为"逆序数组+长度"的内部表示方法
*/
bign operator = (const char* num)
{
len = strlen(num);
for (int i = 0; i < len; i++)
s[i] = num[len - i - 1] - '0';
return *this;
}
//这样做之后,可以支持 x=1234 这样的赋值方式
bign operator=(int num)
{
char s[maxn];
sprintf(s, "%d", num);
*this = s;
return *this;
}
/*
下面两个函数定义完成后,可以支持bign x=100这样的初始化。
*/
bign(int num)
{
*this = num;
}
bign(const char* num)
{
*this = num;
}
//把bign类型的变量变为字符串
//str()就是bign类型变量的成员函数
string str() const //const表明x.str()不会改变x
{
string res = "";
for (int i = 0; i < len; i++)
{
res = (char)(s[i] + '0') + res;
}
if (res == "")
res = "0";
return res;
}
/*
成员函数的定义和普通函数类似,但成员函数可以直接使用结构体中的域
str()函数可以直接用到len和s
对成员函数来说,this是指向当前对象的指针
*/
/*重定义 + 运算符*/
bign operator + (const bign& b)const
{
bign c;
c.len = 0;
for (int i = 0, g = 0; g || i < max(len, b.len); i++) //只要任何一个数还有数字,加法就要继续进行
{
int x = g;
if (i < len)
x += s[i];
if (i < b.len)
x += b.s[i];
c.s[c.len++] = x % 10;
g = x / 10; //处理进位
}
}
/*重定义 += 运算符*/
bign operator +=(const bign& b)
{
*this = *this + b;
return *this;
}
/*重定义 - 运算符*/
bign operator - (const bign& b)const
{
}
/*重定义 -= 运算符*/
bign operator -= (const bign& b)
{
*this = *this - b;
return *this;
}
/*重定义 * 运算符*/
/*重定义 *= 运算符*/
bign operator *=(const bign& b)
{
*this = *this * b;
return *this;
}
/*重定义 / 运算符*/
/*重定义 /= 运算符*/
bign operator /=(const bign& b)
{
*this = *this / b;
return *this;
}
/*重定义 % 运算符*/
/*重定义 %= 运算符*/
bign operator %=(const bign& b)
{
*this = *this % b;
return *this;
}
/*重定义 < 运算符*/
bool operator < (const bign& b)const
{
if (len != b.len) //一开始就比较两个bign的位数,如果不相等则直接返回
return len < b.len;
for (int i = len - 1; i >= 0; i--) //否则比较两个数组的逆序的字典顺序,这样做的前提是两者都没有前导0
{
if (s[i] != b.s[i])
return s[i] < b.s[i];
}
return false;
}
/*利用 < 运算符定义其他的运算符*/
bool operator >(const bign&b)const //重定义 > 运算符
{
return b < *this;
}
bool operator <=(const bign& b)const //重定义 <= 运算符
{
return !(b < *this);
}
bool operator >=(const bign& b)const //重定义 >= 运算符
{
return !(*this < b);
}
bool operator !=(const bign& b)const //重定义 != 运算符
{
return b < *this || *this < b;
}
bool operator ==(const bign& b)const //重定义 == 运算符
{
return !(b < *this) && !(*this < b);
}
};
//重定义运算符>>和<< 让输入输出流直接支持我们的bign结构体
istream& operator >> (istream& in, bign& x)
{
string s;
in >> s;
x = s.c_str();
return in;
}
ostream& operator << (ostream& out, const bign& x)
{
out << x.str();
return out;
}
对于bign的综合应用
利用了c++中类的思想,来修改运算
采用数组的形式存储大数,并按照手算的思想重载运算符
5.3 排序与检索
6174问题
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int get_next(int num)
{
char s[10];
sprintf(s, "%d", num); //把数据存储到字符数组中
int len = strlen(s);
for (int i = 0; i < len; i++) //把大的数字排到后面
{
for (int j = i + 1; j < len; j++)
{
if (s[i] > s[j])
{
char temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}
}
char s1[10];
for (int i = len - 1; i >= 0; i--) //逆序存储
{
s1[len - i -1] = s[i];
}
int a,b;
sscanf(s1, "%d", &a); //将数字串输入到数字变量中
sscanf(s, "%d", &b);
return a - b;
}
int a[1005];
int main()
{
int n;
scanf("%d", &n);
int flag = 0;
printf("%d", n);
a[0] = n;
for (int i = 1; i < 1000; i++)
{
printf("->");
a[i]=get_next(a[i-1]);
printf("%d", a[i]);
for (int j = 0; j < i; j++)
{
if (a[i] == a[j])
{
flag = 1;
break;
}
}
if (flag == 1)
break;
}
return 0;
}
总结:
sprintf(s,"%d",n):把n值按照整数的形式存储到字符数组中
sscanf(s,"%d",&n):把字符数组中的数据按照整数的形式存储到n中
冒泡排序:for (int i = 0; i < len; i++) //把大的数字排到后面 { for (int j = i + 1; j < len; j++) { if (s[i] > s[j]) { char temp = s[i]; s[i] = s[j]; s[j] = temp; } } }
字母重排
#define _CRT_SECURE_NO_WARNINGS
/*
算法思想:
判断两个单词是否可以通过重排得到?
ANS:把各个字母排序,然后直接比较。在读入时就把每个单词按照字母排好序,就不必每次重排了
必须把能重排的单词保存下来再排序?
ANS:没必要,只要在读入字典之后把所有单词排序,就可以每遇到一个满足条件的单词直接输出就好了
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int n;
char word[2000][10];
char sorted[2000][10];
//比较两个字母的大小
int cmp_char(const void* _a, const void* _b)
{
char* a = (char*)_a;
char* b = (char*)_b;
return *a - *b;
}
//比较两个字符串的大小
int cmp_string(const void* _a, const void* _b)
{
char* a = (char*)_a;
char* b = (char*)_b;
return strcmp(a, b);
}
int main()
{
n = 0;
for (;;) //一直输入,直到 "******"
{
scanf("%s", word[n]);
if (word[n][0] == '*')
break;
n++; //n存储了有多少个单词
}
qsort(word, n, sizeof(word[0]), cmp_string); //sizeof(word[0])表示的是数组的大小,而不是word[0]实际存储了多少字符
for (int i = 0; i < n; i++)
{
strcpy(sorted[i], word[i]); //把word[i]的内容复制到sorted[i]中
qsort(sorted[i], strlen(sorted[i]), sizeof(char), cmp_char); //对sorted[i]进行排序,以不改变word[i]的内容
}
char s[10];
while (scanf("%s", s) == 1)
{
qsort(s, strlen(s), sizeof(char), cmp_char); //对输入的字母序列排序
int found = 0;
for (int i = 0; i < n; i++) //从第1个单词到最后一个,进行一一比较
{
if (strcmp(sorted[i], s) == 0) //如果找到了
{
found = 1;
printf("%s ", word[i]);
}
}
if (!found) //没有找到
printf(":(");
printf("\n");
}
return 0;
}
算法思想:
判断两个单词是否可以通过重排得到?
ANS:把各个字母排序,然后直接比较。在读入时就把每个单词按照字母排好序,就不必每次重排了
必须把能重排的单词保存下来再排序?
ANS:没必要,只要在读入字典之后把所有单词排序,就可以每遇到一个满足条件的单词直接输出就好了
qsort()的用法:
qsort()是在stdlib.h中的
使用库函数排序的代码量并不比冒泡排序少,但速度更快
qsort()声明如下:
void qsort(void * base,size_t nmemb,size_t size ,int(*compar)(const void *,const void *));
base,要排序的数组
nmemb,数组中元素的数目
size,每个数组元素占用的内存空间,可使用sizeof函数获得
compar,指向函数的指针也即函数指针。这个函数用来比较两个数组元素,第一个参数大于,等于,小于第二个参数时,分别显示正值,零,负值。
qsort用起来是真的爽。要注意qsort的第二个参数与第三个参数是不一样的。第二个参数代表的是你要排的长度,而第三个参数代表的是你要排的每个元素的空间大小。如果错了,那么排序不成功。
四个基本函数,处理字符串的。
strcmp
strcat
strlen
strcpy
5.4数学基础
注意数学思维的思考了
如果判断两个浮点数a和b是否相等时,应尽量判断fabs(a-b)是否小于一个事先给定的eps,如1e-9
如果有可能,请尽量避免浮点运算
判断三角形内的树的数目:
/*
计算三角形(x0,y0)-(x1,y1)-(x2,y2)的有向面积的两倍
有向面积
P0 P1 P2 的三个顶点按逆时针排列,则有向面积为正
如果是顺时针排列,则为负
三点共线,有向面积为0
*/
double area2(double x0, double y0, double x1, double y1, double x2, double y2)
{
return x0 * y1 + x2 * y0 + x1 * y2 - x2 * y1 - x0 * y2 - x1 * y0;
}
/*
判断O在三角形内部或边界上当且仅当:
SABC==SOAB+SOBC+SOCA
*/
分割土地:
V-E+F=2
V是顶点数,所有线段的端点数加上交点数
E是边数
F是面数(包括外面那个无穷大的面)
double V,E;
int sum1=0;
int sum2 = 0;
for (int i = 0; i <= n - 2; i++)
{
sum1 += i * (n - 2 - i);
sum2 += (i * (n - 2 - i) + 1);
}
V = n + (n * sum1) / 4.0;
E = n + (n * sum2) / 2.0;
错误
答案错:WA(Wrong Answer)
输出格式错:PE(Presentation Error)
超时:TLE(Time Limit Exceeded)
运行错:RE(Runtime Error)
在运行时,除了程序自身异常退出(如除0,栈溢出,非法访问内存,断言为假,main函数返回非0值)外,还可能是因为超过了评测系统的资源约束(如内存限制,最大输出限制)而被强制中止执行。