(一)、框架
(二)、引用
int n = 4;
int & r = n;
r = 4;
cout << r; //输出
4
cout << n; //输出
4
n = 5;
cout << r; //输出5
int n = 100;
const int & r = n;
r = 200; //编译错
n = 300; // 没问题
注:
const T & 和T & 是不同的类型!!!
不能通过常引用去修改其引用的内容
(三)“const”关键字的用法
1 . 定义常量
const int MAX_VAL = 23;
const string SCHOOL_NAME = "Peking University";
2 .定义常量指针
int n,m;
const int * p = & n;
* p = 5; //编译出错
n = 4; //ok
p = &m; //ok, 常量指针的指向可以变化
3 .定义常引用
int n;
const int & r = n;
r = 5; //error
n = 4; //ok
(四)、字符串
#include
#include
using namespace std;
int main ()
{
string str1 = "Hello";
string str2 = "World";
string str3;
int len ;
// 复制 str1 到 str3
str3 = str1;
cout << "str3 : " << str3 << endl;
// 连接 str1 和 str2
str3 = str1 + str2;
cout << "str1 + str2 : " << str3 << endl;
// 连接后,str3 的总长度
len = str3.size();
cout << "str3.size() : " << len << endl;
return 0;
}
(五)结构体
(六)模板
基本框架:
template ret-type func-name(parameter list)
{
// 函数的主体
}
//举例
#include
#include
using namespace std;
template
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
int main ()
{
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;
return 0;
}
insert() 插入一个数
find() 查找一个数
count() 返回某一个数的个数
erase()
(1) 输入是一个数x,删除所有x O(k + logn)
(2) 输入一个迭代器,删除这个迭代器
lower_bound()/upper_bound()
lower_bound(x) 返回大于等于x的最小的数的迭代器
upper_bound(x) 返回大于x的最小的数的迭代器
bitset<10000> s;
~, &, |, ^
>>, <<
==, !=
[]
count() 返回有多少个1
any() 判断是否至少有一个1
none() 判断是否全为0
set() 把所有位置成1
set(k, v) 将第k位变成v
reset() 把所有位变成0
flip() 等价于~
flip(k) 把第k位取反
补充:C++各种数据类型范围
类型 | 范围 |
---|---|
short | -32768~32767 ( 2字节 ) |
unsigned int | 0~4294967295 (4字节) |
int | -2147483648~2147483647 ( 4字节) |
unsigned long | 0~4294967295 (4字节) |
long long ( int ) | -9223372036854775808~9223372036854775807 (4字节) |
float | (-3.4E+38)~(3.4E+38) 4字节 |
double | ( -1.7E+308)~(1.7E+308) 8字节 |
高精度计算中需要处理好以下几个问题:
1数据的接收方法和存贮方法:
数据的接收和存贮:当输人的数很长时,可采用字符串方式输人,这样可输入位数很长的数,利用字符串函数和操作运算,将每一位数取出,存入数组中。
2)高精度数位数的确定:
位数的确定:接收时往往是用字符串的,所以它的位数就等于字符串的长度。
3)进位,借位处理
4)商和余数的求法
商和余数处理:视被除数和除数的位数情况进行处理。
// C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B)
{
if (A.size() < B.size()) return add(B, A);
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); i ++ )
{
t += A[i];
if (i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(t);
return C;
}
// C = A - B, 满足A >= B, A >= 0, B >= 0
vector<int> sub(vector<int> &A, vector<int> &B)
{
vector<int> C;
for (int i = 0, t = 0; i < A.size(); i ++ )
{
t = A[i] - t;
if (i < B.size()) t -= B[i];
C.push_back((t + 10) % 10);
if (t < 0) t = 1;
else t = 0;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
// C = A * b, A >= 0, b > 0
vector<int> mul(vector<int> &A, int b)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; i ++ )
{
if (i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
return C;
}
// A / b = C ... r, A >= 0, b > 0
vector<int> div(vector<int> &A, int b, int &r)
{
vector<int> C;
r = 0;
for (int i = A.size() - 1; i >= 0; i -- )
{
r = r * 10 + A[i];
C.push_back(r / b);
r %= b;
}
reverse(C.begin(), C.end());
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
基本思想:
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在待排序的数列的最前,直到全部待排序的数据元素排完。
(2)排序过程:
初始关键字[49 38 65 97 76 13 27 49]
第一趟排序后13 [38 65 97 76 49 27 49]
第二趟排序后13 27 [65 97 76 49 38 49]
第三趟排序后13 27 38 [97 76 49 65 49]
第四趟排序后13 27 38 49[76 97 65 49]
第五趟排序后13 27 38 49 49 [97 65 76]
第六趟排序后13 27 38 49 49 65 [97 76]
第七趟排序后13 27 38 49 49 65 76 [97]
最后排序结果13 27 38 49 49 65 76 97
#include
using namespace std;
const int MAX = 1001;
int main(){
int num,arr[MAX],temp;
cin >> num;
for(int i=0;i<num;i++){ //输入数据
cin >> arr[i];
}
for(int j = 0;j<num;j++){
int minn = j; //定义最小值下标,默认为开始遍历位数
for(int k=j+1;k<num;k++){
if(arr[minn]>arr[k]){ //找到最小值下标
minn = k;
}
}
if(minn != j){ //使用下表交换位置
temp = arr[j];
arr[j] = arr[minn];
arr[minn] = temp;
}
for(int i=0;i<num-1;i++){ //输出操作细节
cout << arr[i]<<' ';
}
cout <<arr[7]<<endl;
}
return 0;
}
基本方法:
#include
#include
using namespace std;
const int N = 1e6 +10;
int q[N];
//快速排序函数
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if(i<j) swap(q[i],q[j]);
}
//递归
quick_sort(q, l, j);
quick_sort(q, j + 1, r);//注意:此处为 j+1 ,不可写作 i+1;
}
//主函数
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
quick_sort(q,0,n-1);
for(int j=0;j<n;j++) printf("%d ",q[j]);
return 0;
}
基本方法:
#include
#include
using namespace std;
const int maxn= 1e6+10;
//定义两个数组
int q[maxn],tmp[maxn];
//归并函数
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
//递归
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
//归并
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
if (q[i] < q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
//剩余数组导入
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= r) tmp[k ++ ] = q[j ++ ];
//转移到原数组中
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j]; //注意此处必须写为 i <= j;
}
//主函数
int main(){
int num;
scanf("%d",&num);
for(int i=0;i<num;i++) scanf("%d",&q[i]);
merge_sort(q,0,num-1);
for(int i=0;i<num;i++) printf("%d ",q[i]);
return 0;
}
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 右寻:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 左寻:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;//注意此时中间值取 l+r+1>>1
/*(原因是C++通常向下取整,当l取值为r-1时此循环变为死循环)*/
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求(通常为结果精确值后两位)
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
避免使用普通方法,即不可从左到右依次遍历,这种情况下一定会导致超时
#include
#include
using namespace std;
vector<int> s(100010,0);
int main(){
int n,m;
cin>>n>>m;
int a;
for(int i=1;i<=n;i++) { //此处必须注意 i 从 1 开始
cin>>a;
s[i]=s[i-1]+a;
}
while(m--){
int l,r;
cin>>l>>r;
cout<<(s[r]-s[l-1])<<endl;
}
return 0;
}
二维应用:子矩阵的和
#include
#include
using namespace std;
const int N = 1010;
int a[N][N];
int m,n,q;
int main(){
scanf("%d%d%d",&m,&n,&q);
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++){
int n;
scanf("%d",&n);
a[i][j] =a[i-1][j] + a[i][j-1] - a[i-1][j-1]+ n; //输入的同时构造和矩阵
}
while(q--){
int fl,fr,sl,sr;
scanf("%d%d%d%d",&fl,&fr,&sl,&sr);
printf("%d\n",a[sl][sr]-a[sl][fr-1]-a[fl-1][sr]+a[fl-1][fr-1]); //输出要求值,关键在于 fl,fr 加一
}
return 0;
}
查分实质上是前缀和的逆运算,如差分序列 A[i] 表示原始数列 B[i] 与 B[i-1] 的差
#include
using namespace std;
const int maxn = 1e6+10;
int q[maxn]={0},p[maxn]={0};
int m,n;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){ //注意 i 值从 1 开始,i 需要满足小于等于 n
scanf("%d",&q[i]);
p[i] = q[i] -q[i-1];
}
while(m--){
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
p[l] += x;
p[r+1] -= x;
}
for(int i=1;i<=n;i++){
q[i] = q[i-1] + p[i]; //注意 q[i] 的值是 q[i-1] 与 p[i] 的和
printf("%d ",q[i]);
}
return 0;
}
二维差分方法
#include
#include
using namespace std;
const int maxn = 1e3 + 40;
int a[maxn][maxn], b[maxn][maxn];
inline void insert(int x1, int y1, int x2, int y2, int c) { //此处是矩阵运算的核心部分
b[x1][y1] += c;
b[x2 + 1][y1] -= c;
b[x1][y2 + 1] -= c;
b[x2 + 1][y2 + 1] += c;
}
int main(void) {
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
scanf("%d", &a[i][j]);
insert(i, j, i, j, a[i][j]);
}
while(q--){
int x1, y1, x2, y2, c;
scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &c);
insert(x1, y1, x2, y2, c);
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
if(j == m) printf("%d\n", b[i][j]);
else printf("%d ", b[i][j]);
}
return 0;
}
常见问题分类:
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
// 具体问题的逻辑
}
#include
using namespace std;
const int maxn = 1e5 +10;
int a[maxn],s[maxn];
int main(){
int n = 0, res = 0;
cin >> n;
for(int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
for(int i = 0, j = 0; i < n; i ++ )
{
s[a[i]] ++;
while(j <= i && s[a[i]] > 1)
{
s[a[j]] --; //注意此处为a[j]
j ++;
}
res = max(res,i-j+1);
}
cout <<res<<endl;
return 0;
}
#include
using namespace std;
const int maxn = 1e6 +10;
int a[maxn]={0},b[maxn]={0};
int main(){
int na,nb,x,tmp,cha=1;
cin >> na>>nb>>x;
for(int i=0;i<na;i++) cin>>a[i];
for(int i=0;i<nb;i++) cin >>b[i];
int i=0,j=0;
if(a[0]>b[0]) 1;
else{
for(int k=0;k<max(na,nb);k++) swap(a[k],b[k]);
tmp = na;
na = nb;
nb = tmp;
cha = 0;
}
for(i=0,j=nb-1;i<na;i++){
while(b[j]+a[i]>x){
j--;
}
if(a[i]+b[j]==x) break;
}
if(cha) cout <<i<<' '<<j<<endl;
else cout <<j<<' '<<i<<endl;
}
常见问题:
#include
using namespace std;
int main(){
int n,x;
cin >> n;
for(int i=0;i<n;i++){
int ech = 0;
cin >> x;
while(x) x -=( x &(-x)),ech++;
cout << ech << ' ';
}
cout <<endl;
return 0;
}
vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素
// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
int l = 0, r = alls.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1; // 映射到1, 2, ...n
}
// 将所有存在交集的区间合并
void merge(vector<PII> &segs)
{
vector<PII> res;
sort(segs.begin(), segs.end());
int st = -2e9, ed = -2e9;
for (auto seg : segs)
if (ed < seg.first)
{
if (st != -2e9) res.push_back({st, ed});
st = seg.first, ed = seg.second;
}
else ed = max(ed, seg.second);
if (st != -2e9) res.push_back({st, ed});
segs = res;
}
// head 表示头结点的下标
// e[i] 表示节点i的值
// ne[i] 表示节点i的next指针是多少
// idx 存储当前已经用到了哪个点
int head, e[N], ne[N], idx;
// 初始化
void init()
{
head = -1;
idx = 0;
}
// 将x插到头结点
void add_to_head(int x)
{
e[idx] = x, ne[idx] = head, head = idx ++ ;
}
// 将x插到下标是k的点后面
void add(int k, int x)
{
e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ;
}
// 将下标是k的点后面的点删掉
void remove(int k)
{
ne[k] = ne[ne[k]];
}
// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;
// 初始化
void init()
{
//0是左端点,1是右端点
r[0] = 1, l[1] = 0;
idx = 2;
}
// 在节点a的右边插入一个数x
void insert(int a, int x)
{
e[idx] = x;
l[idx] = a, r[idx] = r[a];
l[r[a]] = idx, r[a] = idx ++ ;
}
// 删除节点a
void remove(int a)
{
l[r[a]] = l[a];
r[l[a]] = r[a];
}
// tt表示栈顶
int stk[N], tt = 0;
// 向栈顶插入一个数
stk[ ++ tt] = x;
// 从栈顶弹出一个数
tt -- ;
// 栈顶的值
stk[tt];
// 判断栈是否为空
if (tt > 0)
{
}
单调栈 ——
常见模型:找出每个数左边离它最近的比它大/小的数
int tt = 0;
for (int i = 1; i <= n; i ++ )
{
while (tt && check(stk[tt], i)) tt -- ;
stk[ ++ tt] = i;
}
// hh 表示队头,tt表示队尾
int q[N], hh = 0, tt = -1;
// 向队尾插入一个数
q[ ++ tt] = x;
// 从队头弹出一个数
hh ++ ;
// 队头的值
q[hh];
// 判断队列是否为空
if (hh <= tt)
{
}
// hh 表示队头,tt表示队尾的后一个位置
int q[N], hh = 0, tt = 0;
// 向队尾插入一个数
q[tt ++ ] = x;
if (tt == N) tt = 0;
// 从队头弹出一个数
hh ++ ;
if (hh == N) hh = 0;
// 队头的值
q[hh];
// 判断队列是否为空
if (hh != tt)
{
}
单调队列 ——
常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
while (hh <= tt && check_out(q[hh])) hh ++ ; // 判断队头是否滑出窗口
while (hh <= tt && check(q[tt], i)) tt -- ;
q[ ++ tt] = i;
}
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
//求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == m)
{
j = ne[j];
// 匹配成功后的逻辑
}
}
#include
#include
using namespace std;
const int N = 1e3 +10;
int f[N]; //背包储存值
int n,x;
int v[N],m[N];
int main(){
cin >> n >> x;
for(int i=1;i<=n;i++) cin >>v[i]>>m[i];
for(int i=1;i<=n;i++)
for(int j = x;j>=v[i];j--)
f[j] = max(f[j],f[j-v[i]]+m[i]); //取含i与不含i的最大值
cout << f[x] << endl;
return 0;
}
#include
#include
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
for (int i = 1; i <= n; i ++ )
for (int j = v[i]; j <= m; j ++ )
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
#include
#include
using namespace std;
const int N = 110;
int n, m;
int v[N], w[N], s[N];
int f[N][N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i] >> s[i];
for (int i = 1; i <= n; i ++ )
for (int j = 0; j <= m; j ++ )
for (int k = 0; k <= s[i] && k * v[i] <= j; k ++ )
f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k);
cout << f[n][m] << endl;
return 0;
}
#include
#include
using namespace std;
const int N = 12010, M = 2010;
int n, m;
int v[N], w[N];
int f[M];
int main()
{
cin >> n >> m;
int cnt = 0;
for (int i = 1; i <= n; i ++ )
{
int a, b, s;
cin >> a >> b >> s;
int k = 1;
while (k <= s)
{
cnt ++ ;
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;
k *= 2;
}
if (s > 0)
{
cnt ++ ;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt;
for (int i = 1; i <= n; i ++ )
for (int j = m; j >= v[i]; j -- )
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
#include
#include
using namespace std;
const int N = 110;
int n, m;
int v[N][N], w[N][N], s[N];
int f[N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
{
cin >> s[i];
for (int j = 0; j < s[i]; j ++ )
cin >> v[i][j] >> w[i][j];
}
for (int i = 1; i <= n; i ++ )
for (int j = m; j >= 0; j -- )
for (int k = 0; k < s[i]; k ++ )
if (v[i][k] <= j)
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
cout << f[m] << endl;
return 0;
}
质数(素数):在大于一的整数中只包含1和本身两个约数。
时间复杂度 O(sprt(n)
bool is_prime(int n)
{
if(n<2) return false;
for(int i = 2;i<=n/i; i++) //此处 i<=n/i 表示 i<=sprt(n) 但由于sprt()运行耗时长,不采用此方法;
if(n % i == 0)
return false;
return true;
}
void divide(int x)
{
for(int i=2;i<=n/i;i++) //n中最多只包含一个大于sqrt(n)的质因子,所以不必暴力枚举;
if(n%i==0)
{
int s=0;
while( n%i == 0)
{
n /= i;
s ++ ;
}
printf("%d %d\n",i,s); //最后输出质因数及其在n中的次数;
}
if(n>1) printf("%d %d\n",n,1);
puts("");
}
int primes[N],cnt;
bool st[N];
void get_primes(int n)
{
for(int i = 2;i<=n;i++)
{
if(!st[i){
primes[cnt++] = n;
for(int j=i+i;j<=n;j+=i) st[j] = true; //去除该质数所有的倍数;
}
}
}
void get_primes(int n){
for(int i=2;i<=n;i++)
{
if(!st[i]) primes[cnt ++] = i;
for(int j = 0;primes[j] <= n/i;j++)
{
st[primes[j]*i] = true;
if(i %primes[j] == 0) break; //primes[j]一定是 i的最小质因子
}
}
}