解题过程的小记录,如有错误欢迎指出。
难度:四星(二分算法中的找出第一个大于的数的模板)
题目分析
给出一串数字,要求找出连续的一段数字的和为所要求价值的数字串。如果不存在,则找出大于价值的但浪费最小的数字串。若有多个答案,一起给出。
注意点
- 题中没有指明宝石的价值和该付的钱一定是整数(看了下大佬代码,好像不考虑也可以过)
- 要求给出的答案下标是从1开始的
- 末尾会存在即是所有的都算入也够不上所要求的价值的情况
- 本题有运行超时的风险,最好改用scanf和printf
我的解题过程
思路
- 设置一个数组sum用于保存当前宝石和它之前的宝石价值之和,这样sum就是一个单调递增的数列,可以用二分算法来找出第一个满足值(从本身到末尾的和>=要求的价值)
- 设置一个结构体pairs用于保存每一个宝石满足价值的包括宝石的起点(本身)和终点,以及含入宝石的总价值(如果总价值小于要求的价值,则后续不参与排序,不然从小到大排序会影响答案),符合要求的存入数组p
- 对数组p进行排序,如果是并列的总价值,说明有多个答案,则一起进行输出
bug
- 将二分算法中的left<right写成left<=right,陷入死循环
- 写答案输出的while时忘了更改变量i陷入死循环
代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n;
double m;
vector<double> D;
vector<double> sum;//每一位及其之前的数的和
struct pairs {
int i,j;
double amount;
};
int binarySearch(int pos) {
if (D[pos] >= m || pos == n) return pos;//如果一颗宝石本身就相等或者大于,或者是最后一个,那么直接返回下标
int left = pos + 1;
int right = n;
while (left < right) {
int mid = (left + right) / 2;
double present = sum[mid] - sum[pos - 1];//当前扫描到的切割总额
if (present > m) right = mid;
else left = mid + 1;
}
return left;
}
int comp(pairs p1, pairs p2) {
return p1.amount != p2.amount ? p1.amount < p2.amount : p1.i < p2.i;
}
int main()
{
double s = 0;
cin >> n >> m;
D.push_back(0);//把0位占掉
sum.push_back(0);
for (int i = 1; i <= n; i++) {
double t;
cin >> t;
D.push_back(t);
s += t;
sum.push_back(s);
}
vector<pairs> p;
for (int i = 1; i <= n; i++) {
pairs temp;
temp.i = i;
temp.j = binarySearch(i);
if (sum[temp.j - 1] - sum[i - 1] == m) temp.j--;
temp.amount = sum[temp.j] - sum[i - 1];
if (temp.amount >= m) p.push_back(temp);
}
sort(p.begin(), p.end(), comp);
cout << p[0].i << "-" << p[0].j << endl;
int i = 1;
while (i < p.size() && p[i].amount == p[i - 1].amount) {
cout << p[i].i << "-" << p[i].j << endl;
i++;
}
return 0;
}
dalao的代码
全部代码因版权原因不放出来,大家可以自行去柳神博客购买或者参考晴神的上机笔记~
借鉴点
- 晴神的代码是采用遍历寻找两次,第一次找出最接近价值的方案(分为刚好为价值,或多于价值),第二次循环只要找出和第一次循环得出价值相等的方案就进行输出
- 可以用resize函数,在输入n后重新定义vector的长度
sum.resize(n+1);
- 累加和输入的更简洁的方法
for(int i = 1; i <= n; i++) {
scanf("%d", &sum[i]);
sum[i] += sum[i-1];
}