例题#
排队接水#
知识点:
下标索引的理解,选择排序,原始编号移动,前缀和

解法:
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的表达式#
知识点:递归函数入门
题目:

解法:
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}二分+贪心(最小的最大区间)#

策略:贪心+二分
在一定范围内二分尝试各个最大的连续区间和,判断是否能够达到该最大区间和,
不断向更小的最大区间和二分尝试
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}