技巧
错误原因
Uva1592: 没用对全局变量与部分变量进行清零操作
1.比较大的数组要开在main函数后面否则程序可能无法正常运行
2.使用重定向的文件方式测试数据更加方便
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
3.数组复制
#include<string.h>
memcpy(b,a,sizeof(a)) #将a复制到b中
memset(b,0,sizeof(b)) #reset b as 0
# the two Fun are in the same headfile
4.ctype.h
含有 isalpha() isdigit() toupper() tolower()
5.提升cin读取速度
取消cin与stdin的同步性
ios::sync_with_stdio(false);
6.在使用全局变量的时候千万记住要清空一下啊啊啊啊啊
7.使用vector 等迭代器的时候记得删除操作慎选iteration,用下标较好
8.解决除法过程中的向上取整
(n - 1)/ M + 1
9.优先队列的用法 priority_queue<>
1.对于内置类型
①默认优先级:
通过<操作符可知在整数中元素大的优先级高。
②传入比较结构体,自定义优先级
注意这里有些不同因为是比较a b 正常情况下通过<运算符比较 那么返回定义了>运算符返回 true就是反向了
#include <queue>
using namespace std;
struct cmp{
bool operator ()(int a,int b){ //通过传入不同类型来定义不同类型优先级
return a>b; //最小值优先
}
};
struct cmp{
bool operator ()(int a,int b){
return a<b; //最大值优先
}
};
priority_queue<int, vector<int>, cmp > q
或者简单方法代替
priority_queue<int, vector<int>,greater<int> >qq
2.对于自定义类型
重载了小于运算符即可
#include <queue>
using namespace std;
struct node {
int priority;
int value;
friend bool operator < (const node &a, const node &b) {
return a.priority < b.priority;
}
10.在列表操作中遇到字符串
可以通过一个map将字符串对应成相应的ID数字
11.stringstream的使用
头文件:sstream.h
sting s
对于大段的文章输入可以先使用getline(cin,s) //遇到/n或者EOF终止
之后 stringstream ss(s);
ss>>s; 类似cin 以空格为分割
12.泛型的算法
- accmulate()
可以用于重载了加法的容器中
string str = accmulate(v.cbegin(),v.cend(),string(""));
2.equal()
比较v1中的元素是否与v2中的元素相同 v2至少要大于等于v13.
equal(v1.begin(),v1.end(),v2.begin());
3.copy()
进行元素的拷贝
auto ret = copy(v1.begin(),v1.end(),back_insert(v)); //将v1拷贝到v中
back_insert 定义在中
4.删除相同元素的操作
先使用sort
之后使用unique 将相同的值都放到了最后并且返回这些相同值的指针
之后调用erase即可擦除一个范围的值
读取时不省略空格
使用 getline(cin, str3) 即可不忽略空格读取前后都不忽略
还可以调用getline(cin, str3).eof()判断是否结尾
cin.eof()也可以判定是否结尾
注意浮点数相除时不要搞成了整数,加上小数点k
浮点数不能做相等比较,可以通分一下
枚举要看以下有没有相关性
算法
1.KMP字符串查找
在一个文本串 S 内查找一个模式串 P 的出现位置 (避免暴力搜索)
主要步骤
1.求出模式串的next数组
2.通过模式串数组在文本串中查找符合条件的模式串位置
查找算法实现
int KmpSearch(char* s, char* p)
{
int i = 0;
int j = 0;
int sLen = strlen(s);
int pLen = strlen(p);
while (i < sLen && j < pLen)
{
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if (j == -1 || s[i] == p[j])
{
i++;
j++;
}
else
{
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = next[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
next数组算法实现
//优化过后的next 数组求法
void GetNextval(char* p, int next[])
{
int pLen = strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k])
{
++j;
++k;
//较之前next数组求法,改动在下面4行
if (p[j] != p[k])
next[j] = k; //之前只有这一行
else
//因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
next[j] = next[k];
}
else
{
k = next[k];
}
}
}
后期学习写的完整代码
#include<cstdio>
#include<iostream>
#include<vector>
#include<string>
using namespace std;
vector<int> next_t;
void Getnext(string p)
{
int pLen = p.size();
next_t.push_back(-1);
int k = -1;
int j = 0;
while (j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k])
{
++k;
++j;
next_t.push_back(k);
}
else
{
k = next_t[k]; //向前查找
}
}
}
int KmpSearch(string s, string p)
{
int i = 0;
int j = 0;
int sLen = s.size();
int pLen = p.size();
while (i < sLen && j < pLen)
{
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if (j == -1 || s[i] == p[j])
{
i++;
j++;
}
else
{
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next_t[j]
//next_t[j]即为j所对应的next_t值
j = next_t[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
int main()
{
string a, b;
cin >> a >> b;
next_t.clear();
Getnext(b);
if (KmpSearch(a,b))
{
cout << KmpSearch(a, b)<<endl;
}
return 0;
}
2.并查集与简单的同构查找
主要学习了一下并查集的概念 并查集主要就是两段代码一段查找的一段判断有没有链接在一块,没有的话连在一起
int pre[1000 ];
int find(int x) //查找根节点
{
int r=x;
while ( pre[r] != r ) //返回根节点 r
r=pre[r];
int i=x , j ;
while( i != r ) //路径压缩
{
j = pre[ i ]; // 在改变上级之前用临时变量 j 记录下他的值
pre[ i ]= r ; //把上级改为根节点
i=j;
}
return r ;
}
void join(int x,int y) //判断x y是否连通,
//如果已经连通,就不用管了 如果不连通,就把它们所在的连通分支合并起,
{
int fx=find(x),fy=find(y);
if(fx!=fy)
pre[fx ]=fy;
}
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int pre_s[10100];
struct number
{
int a;
int b;
};
number s1_s[10100];//用于存储第一个图的一些环与链的情况 根节点
number s2_s[10100];//第二个
int find(int x) //寻找根节点的函数 返回根节点
{
int r = x;
while (r != pre_s[r])
{
r = pre_s[r];
}
return r;
}
bool cmp(const number& a, const number& b)
{
if (a.a == b.a)
{
return a.b > b.b;
}
else
{
return a.a > b.b;
}
}
void init(int n)//初始化一下pre_s数组
{
for (int i = 0; i < n; i++)
{
pre_s[i] = i;
}
}
int main()
{
int t, cas = 1;;
scanf("%d", &t);
while (t--)
{
for (int i = 1; i < 10010; i++)//第一次初始化
{
s1_s[i].a = 1; s1_s[i].b = 0;
s2_s[i].a = 1; s2_s[i].b = 0;//最开始每个都是独立的,默认为链
}
bool flag = false;
int n1, m1, n2, m2;
scanf("%d%d", &n1, &m1);
init(n1);//初始化
for (int i = 0; i < m1; i++)
{
int a, b;
scanf("%d%d", &a, &b);
int dx = find(a);
int dy = find(b);
if (dx != dy) //没有存在过就是链存在过就成环了
{
pre_s[dx] = dy;
s1_s[dy].a += s1_s[dx].a;
s1_s[dx].a = 0;//把拉手的孩子数量加起来,下同
}
else s1_s[dy].b = 1;//成环
}
//again
scanf("%d%d", &n2, &m2);
init(n2);
for (int i = 0; i < m2; i++)
{
int a, b;
scanf("%d%d", &a, &b);
int dx = find(a);
int dy = find(b);
if (dx != dy)
{
pre_s[dx] = dy;
s2_s[dy].a += s2_s[dx].a;
s2_s[dx].a = 0;
}
else s2_s[dy].b = 1;
}
if (n1 == n2) {
sort(s1_s + 1, s1_s + n1 + 1, cmp);
sort(s2_s + 1, s2_s + n2 + 1, cmp);//排序,若孩子的数量相同则对是否是环进行排序,这里要注意
for (int i = 0; i < n1; i++)
if (s1_s[i].a != s2_s[i].a || s1_s[i].b != s2_s[i].b) {//判断数量,状态
flag = true;
break;
}
}
if (n1 != n2) flag = true;
if (flag) printf("Case #%d: NO\n", cas++);
else printf("Case #%d: YES\n", cas++);
}
return 0;
}
3.迪杰斯特拉算法
// 邻接矩阵
typedef struct _graph
{
char vexs[MAX]; // 顶点集合
int vexnum; // 顶点数
int edgnum; // 边数
int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;
// 边的结构体
typedef struct _EdgeData
{
char start; // 边的起点
char end; // 边的终点
int weight; // 边的权重
}EData;
/*
* Dijkstra最短路径。
* 即,统计图(G)中"顶点vs"到其它各个顶点的最短路径。
*
* 参数说明:
* G -- 图
* vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。
* prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。
* dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。
*/
void dijkstra(Graph G, int vs, int prev[], int dist[])
{
int i,j,k;
int min;
int tmp;
int flag[MAX]; // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。
// 初始化
for (i = 0; i < G.vexnum; i++)
{
flag[i] = 0; // 顶点i的最短路径还没获取到。
prev[i] = 0; // 顶点i的前驱顶点为0。
dist[i] = G.matrix[vs][i];// 顶点i的最短路径为"顶点vs"到"顶点i"的权。
}
// 对"顶点vs"自身进行初始化
flag[vs] = 1;
dist[vs] = 0;
// 遍历G.vexnum-1次;每次找出一个顶点的最短路径。
for (i = 1; i < G.vexnum; i++)
{
// 寻找当前最小的路径;
// 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
min = INF;
for (j = 0; j < G.vexnum; j++)
{
if (flag[j]==0 && dist[j]<min)
{
min = dist[j];
k = j;
}
}
// 标记"顶点k"为已经获取到最短路径
flag[k] = 1;
// 修正当前最短路径和前驱顶点
// 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
for (j = 0; j < G.vexnum; j++)
{
tmp = (G.matrix[k][j]==INF ? INF : (min + G.matrix[k][j])); // 防止溢出
if (flag[j] == 0 && (tmp < dist[j]) )
{
dist[j] = tmp;
prev[j] = k;
}
}
}
// 打印dijkstra最短路径的结果
printf("dijkstra(%c): \n", G.vexs[vs]);
for (i = 0; i < G.vexnum; i++)
printf(" shortest(%c, %c)=%d\n", G.vexs[vs], G.vexs[i], dist[i]);
}
4.数论
1.最大公因数
//两个数的最大公约数-—辗转相除法
int gcd(int a, int b){
//总是将较小的数放在b中
if(a < b){
swap(a, b);
}
if(b == 0){
return a;
}
else{
return gcd(b, a%b);
}
}
2.最小公倍数等于两数相乘除以最大公因数
观察区间是否满足的波动性问题
看一个区间 [a,b] [c,d] 等是否满足 在 k的波动下找到一组数使得其满足区间
#include<iostream>
#include <vector>
#include<algorithm>
using namespace std;
typedef long long ll;
int main() {
int t;
scanf("%d", &t);
while (t--) {
int a;
ll b;
scanf("%d %lld", &a, &b);
bool flag = true;
vector<ll> vmax, vmin;
for (int i = 0; i != a; i++) {
ll c, d;
scanf("%lld %lld", &c, &d);
if (i == 0)vmax.push_back(d), vmin.push_back(c);
else {
vmin.push_back(max(c, vmin.back() - b)); //当前在这一步所能达到的最小值
vmax.push_back(min(d, vmax.back() + b)); //当前在这一步所能达到的最大值
}
if (vmin.back() > vmax.back())flag = false;
}
if (!flag)printf("NO\n");
else {
vector<ll> ans;
ll now = vmin.back();
ans.push_back(now);
for (int i = 0; i != a - 1; i++) {
vmin.pop_back();
//if (vmin.back() > now + b)flag = false;
now = max(vmin.back(), now - b);
ans.push_back(now);
}
//if (!flag) {
// printf("NO\n");
// continue;
//}
printf("YES\n");
for (ll i = ans.size() - 1; i != -1; i--) {
printf("%d", ans[i]);
if (i)putchar(' ');
}
putchar('\n');
}
}
return 0;
}