1)归并排序:分治思想(分治法是将复杂问题分解为相同或相似的子问题,再递归地求解,利用子问题的解合并为原问题的解的方法),首先确定分界点,再分别递归排序左右,最后归并合二为一。
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e5+5;
using namespace std;
ll n;
ll a[nl];
ll q[nl];
void f(ll l,ll r){
if(l>=r){
return;
}
ll mid;
mid=(l+r)/2;
f(l,mid);
f(mid+1,r);
ll i=l,j=mid+1,k=0;
while(i<=mid&&j<=r){
if(a[i]<a[j]){
q[k]=a[i];
k++;
i++;
}else{
q[k]=a[j];
k++;
j++;
}
}
while(i<=mid){
q[k]=a[i];
k++;
i++;
}
while(j<=r){
q[k]=a[j];
k++;
j++;
}
j=0;
for(ll i=l;i<=r;i++){
a[i]=q[j];
j++;
}
}
int main(){
cin>>n;
ll i,j;
for(i=0;i<n;i++){
cin>>a[i];
}
f(0,n-1);
for(i=0;i<n;i++){
cout<<a[i]<<" ";
}
}
2)日期问题:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool check_valid(int year, int month, int day)
{
if (month == 0 || month > 12) return false;
if (day == 0) return false;
if (month != 2)
{
if (day > days[month]) return false;
}
else
{
int leap = year % 100 && year % 4 == 0 || year % 400 == 0;
if (day > 28 + leap) return false;
}
return true;
}
int main()
{
int a, b, c;
scanf("%d/%d/%d", &a, &b, &c);
for (int date = 19600101; date <= 20591231; date ++ )
{
int year = date / 10000, month = date % 10000 / 100, day = date % 100;
if (check_valid(year, month, day))
{
if (year % 100 == a && month == b && day == c || // 年/月/日
month == a && day == b && year % 100 == c || // 月/日/年
day == a && month == b &&year % 100 == c) // 日/月/年
printf("%d-%02d-%02d\n", year, month, day);
}
}
return 0;
}
3)树状数组:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, m;
int a[N], tr[N];
int lowbit(int x)//表明包含它的下一个位置
{
return x & -x;
}
void add(int x, int v)//用来初始化树状数组,并用来修改树状数组
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += v;//包含x的位置都要+X
}
int query(int x)//前缀和
{
int res = 0;
for (int i = x; i>=1; i -= lowbit(i)) res += tr[i];
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ ) add(i, a[i]);//初始化树状数组,树状数组分很多层,越高层包含数越多
while (m -- )
{
int k, x, y;
scanf("%d%d%d", &k, &x, &y);
if (k == 0) printf("%d\n", query(y) - query(x - 1));
else add(x, y);
}
return 0;
}
4)双指针:
//上一段时间与下一段时间有重复,区间有重复,就可以优化,可以使用双指针。
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e5+5;
using namespace std;
#define x first
#define y second
typedef pair<ll, ll> PII;
PII logs[nl];
ll cnt[nl];
bool st[nl];
ll n,m,k;
ll i,j;
int main(){
cin>>n>>m>>k;
for(ll i=0;i<n;i++){
cin>>logs[i].x>>logs[i].y;
}
sort(logs,logs+n);
for(ll i=0,j=0;i<n;i++){
ll id=logs[i].y;
cnt[logs[i].y]++;//区间开端需要加1
while(logs[i].x-logs[j].x>=m){//判定是不是在区间里
cnt[logs[j].y]--;//区间末尾需要-1
j++;
}
if(cnt[logs[i].y]>=k){
st[id]=true;
}
}
ll res=0;
for(ll i=0;i<=100000;i++){//直接便利所有数即可,很巧妙。
if(st[i]==true){
cout<<i<<endl;
}
}
}
5)BFS:
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
using namespace std;
typedef pair<ll,ll>PII;
ll t;
ll n,m;
#define x first
#define y second
char a[nl][nl];
ll dist[nl][nl];
ll bfs(PII start,PII end){
queue<PII>q;
memset(dist,-1,sizeof(dist));
dist[start.x][start.y]=0;
q.push(start);
ll dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
while(q.size()){
auto t=q.front();
q.pop();
for(ll i=0;i<4;i++){
ll x=t.x+dx[i],y=t.y+dy[i];
if(x<0||x>=n||y<0||y>=m){
continue;
}
if(a[x][y]=='#'){
continue;
}
if(dist[x][y]!=-1){
continue;
}
dist[x][y]=dist[t.x][t.y]+1;
q.push(make_pair(x,y));
if(end==make_pair(x,y)){
return dist[x][y];
}
}
}
return -1;
}
int main(){
cin>>t;
while(t--){
cin>>n>>m;
PII start,end;
ll i,j;
for(i=0;i<n;i++){
for(j=0;j<m;j++) {
cin >> a[i][j];
if(a[i][j]=='E'){
start=make_pair(i,j);
}else if(a[i][j]=='S'){
end=make_pair(i,j);
}
}
}
if(bfs(start,end)==-1){
cout<<"oop!"<<endl;
}else{
cout<<bfs(start,end)<<endl;
}
}
}
7)差分:与前缀和互为补充,是前缀和逆运算,构造差分数组使得原数组为差分数组的前缀和。意义:另数组某个区间内所有数都加一个数c,只需O(1)。另差分数组的区间左端加数c,其区间右端的后一个数减去数c。
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
using namespace std;
ll a[nl][nl];
ll b[nl][nl];
ll s[nl][nl];
void insert(ll x,ll y,ll x1,ll y1,ll z){
b[x][y]+=z;
b[x1+1][y]-=z;
b[x][y1+1]-=z;
b[x1+1][y1+1]+=z;
}
int main(){
ll n,m,k;
cin>>n>>m>>k;
ll i,j;
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cin>>a[i][j];
insert(i,j,i,j,a[i][j]);
}
}
for(i=0;i<k;i++){
ll x,y,x1,y1,z;
cin>>x>>y>>x1>>y1>>z;
insert(x,y,x1,y1,z);
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+b[i][j];
}
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cout<<s[i][j]<<" ";
}
cout<<endl;
}
}
8)前缀和:下标从一开始,方便统一计算。记录了第一个元素到第i个元素的值(一维前缀和)。降低求数组和的时间复杂度,求数组之间的和可以用前缀和数组相减的方式完成。
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
const ll inf=1e9;
using namespace std;
ll a[nl][nl];
ll s[nl][nl];
int main(){
ll n,m,k;
cin>>n>>m>>k;
ll i,j;
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cin>>a[i][j];
}
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];//
}
}
for(i=0;i<k;i++) {
ll x,y,x1,y1;
cin>>x>>y>>x1>>y1;
cout<<s[x1][y1]-s[x-1][y1]-s[x1][y-1]+s[x-1][y-1]<<endl;
}
}
9)二分:
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e5+5;
using namespace std;
ll a[nl];
ll n,m;
int main(){
cin>>n>>m;
ll i,j;
for(i=0;i<n;i++){
cin>>a[i];
}
while(m--){
ll k;
cin>>k;
ll l=0,r=n-1;
while(l<r){
ll mid=(r+l)/2;
if(a[mid]>=k){
r=mid;
}else{
l=mid+1;
}
if(a[r]!=k){
cout<<"-1 -1"<<endl;
}else{
cout<<r<<" ";
l=0,r=n-1;
while(l<r){
ll mid=(r+l+1)/2;
if(a[mid]<=k){
l=mid;
}else{
r=mid-1;
}
}
cout<<r<<endl;
}
}
}
10)DFS
11)DP
12)单链表:
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e5+5;
using namespace std;
ll head,e[nl],ne[nl],idx;
void init(){
head=-1;
idx=0;
}
void head_cha(ll x){
e[idx]=x;
ne[idx]=head;
head=idx;
idx++;
}
void cha(ll k,ll x){
e[idx]=x;
ne[idx]=ne[k];
ne[k]=idx;
idx++;
}
void shan(ll k){
ne[k]=ne[ne[k]];
}
int main(){
ll n;
cin>>n;
init();
while(n--){
char s;
cin>>s;
// cout<<s<<endl;
if(s=='H'){
ll x;
cin>>x;
head_cha(x);
}
if(s=='D'){
ll k;
cin>>k;
if(k==0){
head=ne[head];
}else {
shan(k - 1);
}
}
if(s=='I'){
ll a,b;
cin>>a>>b;
cha(a-1,b);
}
}
for(ll i=head;i!=-1;i=ne[i]){
cout<<e[i]<<" ";
}
}
13)并查集
14)字符串
15)高精度算法:
//高精度加法:将大数先以字符串的形式输入。借用vector数组反向存入数据。按照加法法则,累加并设置进位。
#include<bits/stdc++.h>
using namespace std;
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);//取模12取2
t /= 10;//加法法则,大于十进一
}
if (t) C.push_back(t);
return C;
}
int main(){
string a,b;//数字存字符串内
vector<int>A,B;
cin>>a>>b;
for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');//数组从大数的个位开始存取(把大数的最后一个数存在数组第一个)为了方便计算顺次乘十即可
for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');
vector<int> C = add(A, B);
for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
cout << endl;
return 0;
}
//高精度减法
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e5+5;
using namespace std;
ll i,j;
bool cmp(vector<ll>&A,vector<ll>&B){//判断AB大小
if(A.size()>B.size()){
return true;
}else if(A.size()<B.size()){
return false;
}else{
for(i=A.size()-1;i>=0;i--){//若位数相等,则从最大位开始比较。
if(A[i]>B[i]){
return true;
}else if(A[i]<B[i]){
return false;
}
}
}
return true;
}
vector<ll>sub(vector<ll>&A,vector<ll>&B){//做减法
vector<ll>c;
ll t=0;
for(i=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){//借位要减去1
t=1;
}else{
t=0;
}
}
while(c.size()>1&&c.back()==0){
c.pop_back();
}
return c;
}
int main(){
string a,b;
vector<ll>A,B;
cin>>a>>b;
for(i=a.size()-1;i>=0;i--){
A.push_back(a[i]-'0');
}
for(i=b.size()-1;i>=0;i--){
B.push_back(b[i]-'0');
}
vector<ll>c;
if(cmp(A,B)==true){//判断谁的值更大,本程序默认大的数减去小的数
c=sub(A,B);
}else{
c=sub(B,A);//如果A<B,则应该返回-(B-A);
cout<<'-';
}
for(i=c.size()-1;i>=0;i--){
cout<<c[i];
}
}
//高精度乘法
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e5+5;
using namespace std;
ll i,j;
vector<ll>mul(vector<ll>&A,int b){
vector<ll>c;
ll t=0;
for(i=0;i<A.szie()||t;i++){
if(i<A.size()){
t+=A[i]*b;
}
c.push_back(t%10);
t/=10;
}
while(c.size()>1&&c.back()==0){
c.pop_back();
}
return c;
}
int main(){
string a;
ll b;
cin>>a>>b;
vector<ll>A;
for(i=a.size()-1;i>=0;i--){
A.push_back(a[i]-'0');
}
vector<ll>c=mul(A,b);
for(i=c.size()-1;i>=0;i--){
cout<<c[i];
}
}
双指针算法:一种算法思想。使用这种算法思想一般先暴力枚举,观察i与j有无单调关系,再利用i与j之间规律进行优化,达到降低时间复杂度。
快速排序:利用分治思想,首先确定分界点,再调整区间,最后递归处理左右
#include <iostream>
using namespace std;
const int N = 100010;
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)/2];
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);
}
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 i = 0; i < n; i ++ ) printf("%d ", q[i]);
return 0;
}