跳过正文
  1. 学习笔记/
  2. Language/
  3. C++ Algorithm/
  4. C++ · 基础算法/

·6 分钟·
lyrumu
作者
lyrumu
目录

例题
#


排队接水
#

知识点:

下标索引的理解,选择排序,原始编号移动,前缀和

loading-ag-77

解法:

 1#include <bits/stdc++.h>
 2using namespace std;
 3int main(){
 4    int n;
 5    cin>>n;
 6    double time[n];
 7    int original_index[n];//设立原始编号,很重要
 8    for(int i = 0;i<n;i++){
 9        cin>>time[i];
10        original_index[i] = i+1;
11    }
12    for(int i = 0;i<n;i++){
13        int minindex = i;
14        for(int j = i+1;j<n;j++){
15            if(time[j]<time[minindex]){//利用选择排序从小到大选出索引,完成第一行输出
16                minindex = j;
17            }
18        }
19        cout<<original_index[minindex]<<" ";
20        swap(time[i],time[minindex]);//防止重复,还是需要交换元素
21        swap(original_index[i],original_index[minindex]);//原始编号也要跟着它主人一起移动交换
22    }
23    cout<<endl;
24    double sum = 0;
25
26    double pre[n];//计算每个人除了自己接水,需要额外等待的时间
27    pre[0] = 0;//第一个人额外等待时间为0
28    for(int i = 1;i<n;i++){//从第二个人开始
29        pre[i] = time[i-1]+pre[i-1];//等于他前一个人接水时间加上前一个人额外等待时间
30    }
31
32    for(int i = 0;i<n;i++){//计算等待总时间
33        sum = sum+pre[i];//修改为正确的等待时间
34    }
35    double average = sum/n;
36    cout<<fixed<<setprecision(3)<<average;
37    return 0;
38}

重点在于原始编号按题目要求改变后,能够正确输出


自然数的拆分
#

知识点:

深度优化搜索DFS,全局变量,一些语法和编程习惯

自然数拆分题目

解法:

 1#include <bits/stdc++.h>
 2using namespace std;
 3vector<string> results;//设置全局变量
 4void dfs(int remain,int start,string current){
 5    if(remain==0){
 6        results.push_back(current);//如果remain已经为0,说明完成一种拆分方法,将它存入results
 7        return;
 8    }
 9    for(int i = start;i<=remain;i++){//先从从1到n选一个数
10        string new_current;//开一个新的拆分方法
11        if(current.empty()){
12            new_current = to_string(i);
13        }else{
14            new_current = current+"+"+to_string(i);
15        }
16        dfs(remain-i,i,new_current);//相当于,例如n=7,已经选了一个1,再考虑剩下的6怎么拆......
17    }
18}
19int main(){
20    int n;
21    cin>>n;
22    dfs(n,1,"");//读入
23    sort(results.begin(),results.end());//按字典序升序排列(题目要求)
24    for(int i = 0;i<results.size();i++){
25        if(results[i]==to_string(n)){//去掉最后的n=n
26            results.erase(results.begin()+i);//erase必须要访问变量的地址,不能用results[i]
27            i--;//此题其实不需要i--,但是如果有多个n=n就需要,保持好习惯
28        }
29    }
30    for(int i = 0;i<results.size();i++){
31        cout<<n<<"="<<results[i]<<endl;
32    }
33    return 0;
34}

全局变量在main()函数外,所有函数都可以直接使用这个变量

细节很多,慢慢品……


字符串处理
#

知识点:预留空间,+=的好处,字符到数值的前缀和

题目:

暴龙的白菜

解法:

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define ll long long
 4int main(){
 5    string s;
 6    s.reserve(1000005);
 7    for(int i = 1;s.size()<=1e6;i++){
 8        string temp = to_string(i);
 9        for(int j = 1;j<=i;j++){
10            s += temp;//完成这个大字符串创建
11        }
12    }
13    int t;
14    cin>>t;
15    ll presum[1000005] = {0};
16    for(ll i = 1;i<=1e6;i++){
17        presum[i] = presum[i-1]+(s[i-1]-'0');//甜菜前缀和,直接把字符转换为数值了
18    }
19    while(t--){
20        ll l,r;
21        cin>>l>>r;
22        ll sum = presum[r]-presum[l-1];
23        cout<<sum<<endl;
24    }
25}

求f的表达式
#

知识点:递归函数入门

题目:

f表达式

解法:

 1#include <bits/stdc++.h>
 2using namespace std;
 3double f(double x, int n, int m) {//基本的递归写法,常看常新哦
 4    double result = sqrt(m + x);
 5    if (m < n) {
 6        return f(result, n, m + 1);
 7    } else {
 8        return result;
 9    }
10}
11int main() {
12    int t;
13    scanf("%d", &t);
14    while (t--) {
15        double x;
16        int n;
17        scanf("%lf %d", &x, &n);
18        double result = f(x, n, 1);//每次从m=1开始
19        printf("%.3f\n", result);
20    }
21    return 0;
22}

活动的选择
#

知识点:贪心,区间衔接,sort排序

题目:

活动的选择

解法:

 1#include <bits/stdc++.h>
 2using namespace std;
 3struct timeq{
 4    int begin;
 5    int end;
 6};
 7int main(){
 8    int n;
 9    cin>>n;
10    vector<timeq> activ(n);
11    for(int i = 0;i<n;i++){
12        cin>>activ[i].begin>>activ[i].end;
13    }
14    for(int i = 0;i<n-1;i++){
15        for(int j = 0;j<n-1-i;j++){
16            if(activ[j].end>activ[j+1].end){
17                swap(activ[j],activ[j+1]);//按结束时间从早到晚排序,结束越早的排前面,最后能够排更多个数
18            }
19        }
20    }
21    int cnt = 1;
22    for(int i = 0;i<n;){
23        int j = i+1;
24        while(j<n && activ[i].end>activ[j].begin){//从第一个最早结束的活动开始,向后寻找第一个匹配的活动
25            j++;
26        }
27        //此刻在while结束后找到了一个j
28        if(j<=n-1){
29            cnt++;
30            i = j;
31        }else{
32            break;//若j已经超出活动数量n,直接结束就可以了
33        }
34    }
35    cout<<cnt;
36    return 0;
37}

2的幂次方表示
#

知识点:主要还是递归吧 有点麻烦

题目:在ZJNU的Oline Judge上吧

解法:

 1#include <bits/stdc++.h>
 2using namespace std;
 3string solve(int n){
 4    if(n==1)return "2(0)";//雷霆递归(哭
 5    if(n==2)return "2";
 6    string result = "";
 7    for(int power = 14;power>-1;power--){
 8        int two_power = 1;
 9        for(int j = 0;j<power;j++){
10            two_power = two_power*2;
11        }
12        if(n>=two_power){
13            if(result!=""){
14                result += "+";
15            }
16            if(power==1){
17                result += "2";
18            }else if(power==0){
19                result += "2(0)";
20            }else{
21                result += "2("+solve(power)+")";
22            }
23            n -= two_power;
24        }
25    }
26    return result;
27}
28int main(){
29    int t;
30    cin>>t;
31    while(t--){
32        int n;
33        cin>>n;
34        cout<<solve(n)<<endl;
35    }
36    return 0;
37}

最大公共子串
#

知识点:动态规划

题目:

给定两个字符串,输出其最长公共字串的长度以及这个字串

字符串长度<1000

解法:

 1#include<bits/stdc++.h>
 2using namespace std;
 3int main(){
 4    string s1,s2;
 5    getline(cin,s1);
 6    getline(cin,s2);
 7    int len1 = s1.size();
 8    int len2 = s2.size();
 9    //dp[i][j]表示以s1[i-1]和s2[j-1]结尾的两个字串的最长公共子串长度
10    vector<vector<int>> dp(len1+1,vector<int>(len2+1,0));
11    int maxlen = 0;
12    int endpos = 0;
13    for(int i = 1;i<=len1;i++){
14        for(int j = 1;j<=len2;j++){
15            if(s1[i-1]==s2[j-1]){
16                dp[i][j] = dp[i-1][j-1]+1;
17                if(dp[i][j]>maxlen){
18                    maxlen = dp[i][j];
19                    endpos = i-1;
20                }
21            }else{
22                dp[i][j] = 0;
23            }
24        }
25    }
26    cout<<maxlen<<endl;
27    if(maxlen>0){
28        string result = s1.substr(endpos-maxlen+1,maxlen);
29        //至于为什么是endpos-maxlen+1
30        //实在理解不了就举个例子,以个别情况推出一般情况
31        cout<<result<<endl;
32    }else{
33        cout<<""<<endl;
34    }
35    return 0;
36}

二分+贪心(最小的最大区间)
#

杰哥的难题2

策略:贪心+二分

在一定范围内二分尝试各个最大的连续区间和,判断是否能够达到该最大区间和,

不断向更小的最大区间和二分尝试

bool check:

若在当前要判断的mid值下,最少需要的分段数小于等于给定的分段数,就代表能够满足条件

 1#include<bits/stdc++.h>
 2using namespace std;
 3bool check(int mid,const vector<int>& cost,int n,int m){//数组使用&避免复制造成的浪费
 4    int cnt = 1;//至少分成一段
 5    int sum = 0;
 6    for(int i = 0;i<n;i++){
 7        if(cost[i]>mid){
 8            return false;
 9        }
10        if(sum+cost[i]>mid){
11            sum = cost[i];//重新开始累加,注意这里别写成"sum = 0"
12            cnt++;//所需最少分段数+1
13            if(cnt>m){
14                return false;
15            }
16        }else{
17            sum+=cost[i];
18        }
19    }
20    return true;
21}
22int main(){
23    ios::sync_with_stdio(false);
24    cin.tie(0);
25    cout.tie(0);
26    int n,m;
27    while(cin>>n>>m){
28        vector<int> cost(n);
29        int sum = 0;int maxc = 0;
30        for(int i = 0;i<n;i++){
31            cin>>cost[i];
32            maxc = max(maxc,cost[i]);
33            sum+=cost[i];
34        }
35        int l = maxc;
36        int r = sum;
37        int result = 0;
38        while(l<=r){
39            int mid = (l+r)/2;//令最大连续区间和为mid,判断是否可行
40            //可以写成"mid = l+(r-l)/2"防止溢出
41            if(check(mid,cost,n,m)){
42                result = mid;
43                r = mid-1;//向更小的最大区间和二分判断
44            }else{
45                l = mid+1;
46            }
47        }
48        cout<<result<<'\n';
49    }
50    return 0;
51}

二分+差分(借教室)
#

借教室

策略:利用二分查找第一个不能满足的订单

利用实时的差分判断当前是否符合

 1#include<bits/stdc++.h>
 2using namespace std;
 3#define ll long long
 4struct borrow{
 5    ll d;
 6    ll s;
 7    ll t;
 8};
 9ll n,m;
10vector<ll> rooms;//初始每天的空余房间
11vector<borrow> orders;//所有订单
12bool check(int k){//判断前k份订单能否满足
13    vector<ll> diff(n+2,0);//差分数组
14    for(int i = 1;i<=k;i++){
15        diff[orders[i].s]+=orders[i].d;
16        diff[orders[i].t+1]-=orders[i].d;
17    }
18    ll need = 0;
19    for(int i = 1;i<=n;i++){
20    //在只考虑前k个订单的前提下,遍历n天,看看是否全部能够满足。若不能,就说明不能满足前K个订单
21        need+=diff[i];
22        if(need>rooms[i]){
23            return false;
24        }
25    }
26    return true;
27}
28int main(){
29    ios::sync_with_stdio(false);
30    cin.tie(nullptr);
31    cin>>n>>m;
32    rooms.resize(n+1);
33    for(ll i = 1;i<=n;i++){
34        cin>>rooms[i];
35    }
36    orders.resize(m+1);
37    for(ll i = 1;i<=m;i++){
38        cin>>orders[i].d>>orders[i].s>>orders[i].t;
39    }
40    ll l = 1;ll r = m;ll ans = -1;
41    while(l<=r){
42        ll mid = (l+r)/2;
43        if(!check(mid)){
44            ans = mid;
45            r = mid-1;//继续查找第一个不满足的订单
46        }else{
47            l = mid+1;
48        }
49    }
50    if(ans==-1){
51        cout<<0<<endl;
52    }else{
53        cout<<-1<<endl;
54        cout<<ans<<endl;
55    }
56    return 0;
57}

二分+最大加权平均
#

最大加权平均

注意,严格来说,整体最大加权平均不等于单个最大性价比中挑选最优的

 1#include<bits/stdc++.h>
 2using namespace std;
 3const int maxn = 10005;
 4int n,k;
 5vector<pair<int,int>> cv(maxn);
 6double arr[maxn];
 7bool check(double x){
 8    for(int i = 0;i<n;i++){
 9        arr[i] = cv[i].second-x*cv[i].first;
10    }
11    sort(arr,arr+n,greater<double>());
12    double sum = 0;
13    for(int i = 0;i<k;i++){
14        sum+=arr[i];
15    }
16    return sum>=0;
17}
18int main(){
19    int t;
20    cin>>t;
21    while(t--){
22        cin>>n>>k;
23        for(int i = 0;i<n;i++){
24            cin>>cv[i].first>>cv[i].second;
25        }
26        double l = 0,r = 1e7,ans = 0;
27        for(int i = 0;i<75;i++){
28            double mid = (l+r)/2;
29            if(check(mid)){
30                ans = mid;
31                l = mid;//double型,小心不要写成"l = mid+1"
32            }else{
33                r = mid;
34            }
35        }
36        cout<<int(ans)<<endl;
37    }
38    return 0;
39}