初学算法没多久,队长就给我们讲了 S T L STL STL 中的 sort
#include
sort(int*,int*,bool);
不知道这样写是不是不太合适 不过总而言之,sort的强大之处在于第三个参数可以自己指定排序规则…great…结构体…甚至可以自己写 l a m b d a lambda lambda e x p r e s s i o n s expressions expressions…
不过我现在还不太会…先挖个坑好了…
思想基于 DNC
(分而治之)。
划分区间的时候使用双指针…(之前《啊哈!算法》也看到过…)
quick_sort
#include
#include
using namespace std;
const int MAX_N = 1e5 + 10;
int n,arr[MAX_N];
void quick_sort(int l,int r){
if(l>=r)
return;
int x = arr[(l+r)>>1],i = l-1,j = r+1;
while(i<j){
/*两个哨兵指针*/
do i++;while(arr[i]<x);
do j--;while(arr[j]>x);
if(i<j)
arr[i]^=arr[j]^=arr[i]^=arr[j];}
quick_sort(l,j);
quick_sort(j+1,r);}/*分治左右*/
int main(void){
scanf("%d",&n);
for(int i=0;i<n;++i)
scanf("%d",&arr[i]);
quick_sort(0,n-1);
for(int i=0;i<n;++i)
printf("%d ",arr[i]);
return 0;}
选择问题,其实就是求 第k小/大的数
你当然可以 先排序之后再输出这个数 ,就是下面这样:
#include
#include
using namespace std;
const int MAX_N = 1e5 + 10;
int n,k,arr[MAX_N];
void quick_sort(int l,int r){
if(l>=r)
return;
int x = arr[(l+r)>>1],i = l-1,j = r+1;
while(i<j){
do i++;while(arr[i]<x);
do j--;while(arr[j]>x);
if(i<j)
arr[i]^=arr[j]^=arr[i]^=arr[j];}
quick_sort(l,j);
quick_sort(j+1,r);}
int main(void){
scanf("%d%d",&n,&k);
for(int i=0;i<n;++i)
scanf("%d",&arr[i]);
quick_sort(0,n-1);
printf("%d",arr[k-1]);
return 0;}
不过事实上,你也可以维护一个 k k k 这么长的区间,使用 优先队列
即可。但分析一下,当 k = n 2 k = \frac{n}{2} k=2n (也即是所求为中位数) 时,好像就没有那么好用了。不过也有优化的方法!详见 《数据结构与算法》 那本黑书吧!
#include
#include
#include
using namespace std;
const int MAX_N = 1e5 + 10;
int n,k,arr[MAX_N];
int main(void){
priority_queue<int> q;
scanf("%d%d",&n,&k);
for(int i=0;i<n;++i)
scanf("%d",&arr[i]);
for(int i=0;i<k;++i)
q.push(arr[i]);
for(int i=k;i<n;++i)
if(q.top() > arr[i]){
q.pop();
q.push(arr[i]);}
printf("%d",q.top());
return 0;}
草,这种问题…已经有官方解答了吗么…
#include
#include
#include
using namespace std;
const int MAX_N = 1e5 + 10;
int n,k,arr[MAX_N];
int main(void){
scanf("%d%d",&n,&k);
for(int i=0;i<n;++i)
scanf("%d",&arr[i]);
nth_element(arr,arr+k-1,arr+n);
printf("%d",arr[k-1]);
return 0;}
分成两个部分 (DNC)
,分别排序,最终合二为一。
Yxc老师顺便讲到了 排序算法的稳定性 其实就是相同元素的相对顺序是否发生改变,发生改变就是不稳定的,例如 快速排序
merge sort
#include
#include
using namespace std;
const int MAX_N = 1e5 + 10;
int n,k,arr[MAX_N],tmp[MAX_N];
void merge_sort(int l,int r){
if(l>=r)
return;
int mid = (l+r)>>1;
merge_sort(l,mid);
merge_sort(mid+1,r);/*分治左右*/
int k = 0,i = l,j = mid+1;
while(i<=mid && j<=r)
if(arr[i]<=arr[j])
tmp[k++] = arr[i++];
else
tmp[k++] = arr[j++];
while(i<=mid)
tmp[k++] = arr[i++];
while(j<=r)
tmp[k++] = arr[j++];
for(int i=l,j=0;i<=r;++i,++j)
arr[i] = tmp[j];}/*归并(合二为一)*/
int main(void){
scanf("%d",&n);
for(int i=0;i<n;++i)
scanf("%d",&arr[i]);
merge_sort(0,n-1);
for(int i=0;i<n;++i)
printf("%d ",arr[i]);
return 0;}
逆序对数量
M i n m a x Minmax Minmax
int BiSL(int l,int r){
while(l<r){
int mid = l+r >> 1;
if(check(mid))
r = mid;
else
l = mid + 1;}
return l;}
M a x m i n Maxmin Maxmin
int BiSR(int l,int r){
while(l<r){
int mid = l+r+1 >> 1;
if(!check(mid))
r = mid - 1;
else
l = mid;}
return l;}
e p s eps eps 的值取决于题目
若要保留 n 位小数,取 1 0 − ( n + 2 ) 10^{-(n+2)} 10−(n+2)
const double eps = 1e-8;
double BiSF(double l,double r){
while(r-l>eps){
double mid = (l+r)/2.0;
if(check(mid))
r = mid;
else
l = mid;}
return l;}
二分-数的范围
#include
#include
using namespace std;
const int MAX_N = 1e5+7;
int n,m,arr[MAX_N];
int main(void){
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i)
scanf("%d",&arr[i]);
while(m--){
int x;
scanf("%d",&x);
int l = 0,r = n-1;
while(l<r){
int mid = l+r >> 1;
if(arr[mid]>=x)
r = mid;
else
l = mid+1;}
if(arr[l]!=x)
printf("-1 -1\n");
else{
printf("%d ",l);
int l = 0,r = n-1;
while(l<r){
int mid = l+r+1 >> 1;
if(!(arr[mid]<=x))
r = mid-1;
else
l = mid;}
printf("%d\n",l);}}
return 0;}
实数域二分-数的三次方根
#include
#include
using namespace std;
const double eps = 1e-8;
int main(void){
double x;
scanf("%lf",&x);
double l = -100.0,r = 100.0;
while(r-l>eps){
double mid = (l+r)/2.0;
if(mid*mid*mid>=x)
r = mid;
else
l = mid;}
printf("%.6lf",l);
return 0;}
加 减 乘 除
毕竟 python 没有数据长度限制嘛
print(int(input() + int(input())))
print(int(input() - int(input())))
print(int(input() * int(input())))
a = int(input())
b = int(input())
print(a / b)
print(a % b)
1.考虑到方便进位,倒序存放(否则会增加移动的花费)
2.如果最高位进位,,记得位数加一
下面这个是之前写的…
#include
using namespace std;
const int MAX_N = 1000010;
int main(void){
string a,b;
int left[MAX_N],right[MAX_N],ans[MAX_N];
cin >> a >> b;
int i,len,lena = a.length(),lenb = b.length();
for(i=0;i<lena;++i)
left[lena-i] = a[i] - '0';
for(i=0;i<lenb;++i)
right[lenb-i] = b[i] - '0';
len = max(lena,lenb);
for(i=1;i<=len;++i){
ans[i] += left[i] + right[i];
ans[i+1] = ans[i]/10;
ans[i] %= 10;}
if(ans[len+1]>0)
++len;
for(i=len;i>0;i--)
cout << ans[i];
return 0;}
加一点细节大概就是这样…
#include
#include
#include
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 ans = 0;
for(int i=0;i<A.size();++i){
ans += A[i];
if(i<B.size())
ans += B[i];
C.push_back(ans%10);
ans/=10;}
if(ans)
C.push_back(ans);
return C;}
int main(void){
string a,b;
vector<int> A,B;
cin >> a >> b;
for(int i=a.length()-1;i>=0;--i)
A.push_back(a[i]-'0');
for(int i=b.length()-1;i>=0;--i)
B.push_back(b[i]-'0');
auto C = add(A,B);
for(int i=C.size()-1;i>=0;--i)
printf("%d",C[i]);
return 0;}
1.注意判断两个数字的大小
2.除去前导零
#include
#include
#include
using namespace std;
bool cmp(vector<int> &A,vector<int> &B){
if(A.size()!=B.size())
return A.size() > B.size();
for(int i=A.size()-1;i>=0;--i)
if(A[i]!=B[i])
return A[i] > B[i];
return true;}
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);
t =(t<0)?1:0;}
while(C.size()>1&&!C.back())
C.pop_back();
return C;}
int main(void){
string a,b;
vector<int> A,B;
cin >> a >> b;
for(int i=a.length()-1;i>=0;--i)
A.push_back(a[i]-'0');
for(int i=b.length()-1;i>=0;--i)
B.push_back(b[i]-'0');
vector<int> C;
if(cmp(A,B))
C = sub(A,B);
else
C = sub(B,A),cout << "-";
for(int i=C.size()-1;i>=0;--i)
printf("%d",C[i]);
return 0;}
#include
#include
#include
using namespace std;
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;}
while(C.size()>1&&!C.back())
C.pop_back();
return C;}
int main(void){
string a;
int b;
vector<int> A;
cin >> a >> b;
for(int i=a.length()-1;i>=0;--i)
A.push_back(a[i]-'0');
auto C = mul(A,b);
for(int i=C.size()-1;i>=0;--i)
printf("%d",C[i]);
return 0;}
#include
#include
#include
#include
using namespace std;
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())
C.pop_back();
return C;}
int main(void){
string a;
int b,r;
vector<int> A;
cin >> a >> b;
for(int i=a.length()-1;i>=0;--i)
A.push_back(a[i]-'0');
auto C = div(A,b,r);
for(int i=C.size()-1;i>=0;--i)
printf("%d",C[i]);
printf("\n%d",r);
return 0;}
S n = a n + S n − 1 S_n = a_n + S_{n-1} Sn=an+Sn−1 事实上,初学递归的时候我还见到过不少这样的练习题…
#include
#include
using namespace std;
const int MAX_N = 1e5+7;
int n,m,l,r,arr[MAX_N],S[MAX_N];
int main(void){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&arr[i]);
S[i] = S[i-1] + arr[i];}
while(m--){
scanf("%d%d",&l,&r);
printf("%d\n",S[r]-S[l-1]);}
return 0;}
2.修改
当我们需要对整个区间进行修改的时候,我们可以使用到第二个公式所用的到结构,将和式化简成单个值的修改。
例如,有一个数列 a n = { 1 , 2 , 3 , 4 , 5 , 6 , 7 } a_n = \{1,2,3,4,5,6,7\} an={ 1,2,3,4,5,6,7} 其差分数列自然是 d n = { 1 , 1 , 1 , 1 , 1 , 1 , 1 } d_n = \{1,1,1,1,1,1,1\} dn={ 1,1,1,1,1,1,1} 如果我们要对 [ 3 , 5 ) \left[3,5\right) [3,5) 加上一,也就是 a n = { 1 , 2 , 4 , 5 , 5 , 6 , 7 } a_n = \{1,2,4,5,5,6,7\} an={ 1,2,4,5,5,6,7} 差分数列就变成了 d n = { 1 , 1 , 2 , 1 , 0 , 1 , 1 } d_n = \{1,1,2,1,0,1,1\} dn={ 1,1,2,1,0,1,1} 注意到只是将 d 3 d_3 d3与 d 5 d_5 d5进行了修改,左端点加 1 ,右端点减 1 。这样也将原本 O ( n ) \Omicron(n) O(n) 的修改转换成了 O ( 1 ) \Omicron(1) O(1) 。来看一道题目吧! 差分
#include
#include
using namespace std;
const int MAX_N = 1e6+7;
int n,m,l,r,c,arr[MAX_N],d[MAX_N];
int main(void){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&arr[i]);
d[i] = arr[i] - arr[i-1];}
while(m--){
scanf("%d%d%d",&l,&r,&c);
d[l] += c;
d[r+1] -= c;}
for(int i=1;i<=n;++i){
d[i] += d[i-1];
printf("%d ",d[i]);}
return 0;}
大致明白了这个思路之后,理解 二维形式 的两者也相当轻松啦!二维前缀和 实际上看作一个子矩阵即可,若要求其前缀和,等价于上一个前缀和加上三条新的边即可…但是这样并不能很好的表示出来…最终展示的经典做法事实上可以看作 容斥原理 。
#include
using namespace std;
const int MAX_N = 1e4+7;
int n, m, q,s[MAX_N][MAX_N];
int main(void){
scanf("%d%d%d", &n, &m, &q);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
scanf("%d", &s[i][j]);
s[i][j] += s[i-1][j]+s[i][j-1]-s[i-1][j-1];}
while(q--){
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n",s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);}
return 0;}
二维差分
#include
#include
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], b[N][N];
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(){
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;
cin >> 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];
printf("%d ", b[i][j]);}
puts("");}
return 0;}
之后再补上一张图来解释这些算式叭…
基本上都是 O ( n ) \Omicron(n) O(n) 的时间的一种算法,常见的是 扫描 。 例如KMP啥的…
略
#include
#include
#include
using namespace std;
int main(void){
char a[1007];
scanf("%[^\n]",a);
int n = strlen(a);
for(int i=0;i<n;++i){
int j = i;
while(j<n&&a[j]!=' ')
++j;
for(int k=i;k<j;++k)
cout << a[k];
cout << endl;
i = j;}
return 0;}
#include
using namespace std;
const int N = 100010;
int n;
int q[N], s[N];
int main(void){
scanf("%d",&n);
for (int i=0;i<n;++i)
scanf("%d", &q[i]);
int res = 0;
for (int i=0,j=0;i<n;++i){
++s[q[i]];
while (j<i&&s[q[i]]>1)
--s[q[j++]];
res = max(res,i-j+1);}
cout << res << endl;
return 0;}
n>>1&i++ //求二进制的表达方式
n&(-n) //利用补码的概念易得
实际上等价于 n&(~n+1)
二进制1的个数
一下就想到了linked to leetcode
#include
using namespace std;
#define ll long long
int hammingWeight(ll n) {
int cnt = 0;
while(n){
++cnt;
n &= n-1;}
return cnt;}
const int MAX_N = 1e6 + 10;
ll n,arr[MAX_N];
int main(void){
scanf("%lld",&n);
for(int i=0;i<n;++i){
cin >> arr[i];
arr[i] = hammingWeight(arr[i]);}
for(int i=0;i<n;++i)
cout << arr[i] << " ";
return 0;}
vector<int> alls;
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
排序后存在单调性,二分查找即可。 u p p e r _ b o u n d ( ) upper\_bound() upper_bound()
int find(int 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 l;}
或者直接使用 S T L STL STL
auto p1 = upper_bound(a.begin(),a.end(),(PII){
l,-INF});
auto p2 = upper_bound(a.begin(),a.end(),(PII){
r, INF});
cout << p2 -> second - p1 -> second << endl;
区间和
注:本题数据范围
− 1 0 9 ≤ x ≤ 1 0 9 −10^9≤ x ≤10^9 −109≤x≤109,
1 ≤ n , m ≤ 1 0 5 1 ≤n,m≤10^5 1≤n,m≤105,
− 1 0 9 ≤ l ≤ r ≤ 1 0 9 −10^9≤l≤r≤10^9 −109≤l≤r≤109,
− 10000 ≤ c ≤ 10000 −10000≤c≤10000 −10000≤c≤10000
#include
#include
#include
using namespace std;
typedef pair<int, int> PII;
const int N = 300010;
int n, m;
int a[N], s[N];
vector<int> alls;
vector<PII> add, query;
int find(int 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 l;}
int main(void){
cin >> n >> m;
for (int i = 0; i < n; i ++ ){
int x, c;
cin >> x >> c;
add.push_back({
x, c});
alls.push_back(x);}
for (int i = 0; i < m; i ++ ){
int l, r;
cin >> l >> r;
query.push_back({
l, r});
alls.push_back(l);
alls.push_back(r);}
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
for (auto item : add){
int x = find(item.first);
a[x] += item.second;}
for (int i = 1; i <= alls.size(); i ++ ) s[i] = s[i - 1] + a[i];
for (auto item : query){
int l = find(item.first), r = find(item.second);
cout << s[r] - s[l - 1] << endl;}
return 0;}
#include
#include
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 300010;
long long a[N],s[N];
int main(void){
ios::sync_with_stdio(false);
map<int,ll> mp;
int n, m; cin >> n >> m;
for(int i = 0; i < n; i ++ ){
int index, val; cin >> index >> val;
mp[index] += val;}
vector<PII> query;
for(int i = 0; i < m; i ++ ){
int index1, index2; cin >> index1 >> index2;
query.push_back({
index1, index2});
mp[index1] += 0, mp[index2] += 0;}
int k = 1;
map<int, int> id;
for(auto &[index, val] : mp){
id[index] = k;
a[k] = val;
s[k] = s[k - 1] + a[k];
k++;}
for(auto &[l, r] : query){
l = id[l], r = id[r];
cout << s[r] - s[l - 1] << endl;}
return 0;}
vector<int>::iterator unique(vector<int> &a){
int j = 0;
for (int i = 0; i < a.size(); i ++ )
if (!i || a[i] != a[i - 1])
a[j++] = a[i];
return a.begin() + j;}
按照区间左端点合并
#include
#include
#include
#define INF 2e9
using namespace std;
typedef pair<int, int> PII;
void merge(vector<PII> &segs){
vector<PII> res;
sort(segs.begin(), segs.end());
int st = -INF, ed = -INF;
for (auto seg : segs)
if (ed < seg.first){
if (st != -INF)
res.push_back({
st, ed});
st = seg.first,ed = seg.second;}
else
ed = max(ed, seg.second);
if (st != -INF)
res.push_back({
st, ed});
segs = res;}
int main(void){
int n;
scanf("%d", &n);
vector<PII> segs;
for (int i=0;i<n;++i){
int l, r;
scanf("%d%d",&l,&r);
segs.push_back({
l,r});}
merge(segs);
cout << segs.size() << endl;
return 0;}