题意:
有 n 位同学同时找老师答疑。每位同学都预先估计了自己答疑的时间。
老师可以安排答疑的顺序,同学们要依次进入老师办公室答疑。 一位同学答疑的过程如下:
首先进入办公室,编号为 i的同学需要 si 毫秒的时间。
然后同学问问题老师解答,编号为 i 的同学需要 ai 毫秒的时间。
答疑完成后,同学很高兴,会在课程群里面发一条消息,需要的时间可 以忽略。
最后同学收拾东西离开办公室,需要 ei 毫秒的时间。一般需要 10 秒、20 秒或 30 秒,即 ei 取值为 1000,20000 或 30000。
一位同学离开办公室后,紧接着下一位同学就可以进入办公室了。
答疑从 0 时刻开始。老师想合理的安排答疑的顺序,使得同学们在课程群 里面发消息的时刻之和最小。
输入输出:
输入描述:
输入第一行包含一个整数 n,表示同学的数量。
接下来 n 行,描述每位同学的时间。其中第 i 行包含三个整数 si, ai, ei,意义如上所述。
其中有 ,1≤n≤1000,1≤si≤60000,1≤ai≤106,ei∈10000,20000,30000。即 ei 一定是 10000、20000、30000之一。
输入样例:
3
10000 10000 10000
20000 50000 20000
30000 20000 30000
输出描述:
输出一个整数,表示同学们在课程群里面发消息的时刻之和最小是多少。
输出样例:
280000
本题是一个贪心问题,要想使得所有的时刻之和最小,就要使得每一个时刻尽可能少,根据题目我们可以得到每位同学的时刻=每位同学的等待时间+进门时间+答疑时间。
由于答疑时间是已知的,要使得每位同学的时刻最小,那么就要使得每位同学的等待时间最小。如何使得等待时间最小的呢?
如果这么考虑这道题是做不出来的,我们应该考虑的是使得每位同学等待的时间和最小。
每位同学的时刻 = 每位同学的等待时间 + 进门时间 + 答疑时间
= 前一位同学的等待时间 + 前一位同学的进门时间 + 前一位同学的答疑时间 + 前一位同学的收拾东西的时间 + 进门时间 + 答疑时间
使用贪心算法后,我们可以得出结论是,每位同学的等待时间(前一位同学的等待时间 + 前一位同学的进门时间 + 答疑时间 + 前一位同学的收拾东西的时间)最小。
但是如果相同的时候,两者前后关系是什么?
即:我们选取最小的进门时间+答疑时间+收拾东西时间之和最小的人在前,且当进门时间 + 答疑时间 + 收拾东西时间的和相同时,选择最小的进门时间 + 答疑时间。
代码:
#include
#include
#include
#include
using namespace std;
const int N = 1010;
int n;
struct Stu
{
int inD;
//进门所需时间
int answQ;
//答疑所需时间
int outD;
//收拾东西所需时间
int sum1;
//贪心准则1=进门时间+答疑时间+收拾东西时间
int sum2;
//贪心准则2=进门时间+答疑时间+收拾东西时间
} stu[N];
//贪心准则
bool cmp(Stu st1, Stu st2)
{
if(st1.sum1 != st2.sum1) return st1.sum1 < st2.sum1;
else return st1.sum2 < st2.sum2;
}
int main()
{
//输入数据
scanf("%d", &n);
for(int i = 0; i < n; i ++ )
{
scanf("%d%d%d", &stu[i].inD, &stu[i].answQ, &stu[i].outD);
//标准生成
stu[i].sum1= stu[i].inD + stu[i].answQ+stu[i].outD;
stu[i].sum2 = stu[i].inD + stu[i].answQ;
}
//贪心过程及结果计算
sort(stu, stu + n, cmp);
long long res = 0, t = 0;
for(int i = 0; i < n; i ++ )
{
t += stu[i].sum2;
res += t;
t += stu[i].outD;
}
cout << res << endl;
return 0;
}
这道题是基于前缀和的模拟题,我们按照要求进行模拟即可。
题意:
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
黄金分割数 0.618 与美学有重要的关系。舞台上报幕员所站的位置大约就是舞台宽度的 0.618 处,墙上的画像一般也挂在房间高度的 0.618 处,甚至股票的波动据说也能找到 0.618 的影子....
黄金分割数是个无理数,也就是无法表示为两个整数的比值。0.618 只是它的近似值,其真值可以通过对 5 开方减去 1 再除以 2 来获得,我们取它的一个较精确的近似值:0.618034 。
有趣的是,一些简单的数列中也会包含这个无理数,这很令数学家震惊!
1 3 4 7 11 18 29 47.... 称为“鲁卡斯队列”。它后面的每一个项都是前边两项的和。
如果观察前后两项的比值,即:1\3 3\4 4\7 7\11 11\18...会发现它越来越接近于黄金分割数!
你的任务就是计算出从哪一项开始,这个比值四舍五入后已经达到了与 0.618034 一致的精度。
请写出该比值。格式是:分子/分母。比如:29/47。
输入描述:
无
输入样例:
无
输出描述:
输出一个整数,表示该比值。格式是:分子/分母。比如:29/4729/4729/47。
输出样例:
无
#include
#include
#include
using namespace std;
double a[51] = { 1,3 };
void init()
{
for (int i = 2; i < 50; i++)
{
a[i] = a[i - 1] + a[i - 2];
}
}
string comp()
{
for (int i = 0; i < 50; i++)
{
double b = a[i] / a[i + 1];
if (abs(b - 0.618034) <= 0.000001)
{
stringstream s1;
s1 << a[i] << "/" << a[i + 1];
string s;
s1>>s;
return s;
}
}
}
int main()
{
init();
string ans=comp();
cout<<ans<<endl;
return 0;
}
stringstream 类:
这里给出的 C++ 代码描述,会写的相对繁琐一些,目的是为了给大家讲一种新的字符串使用技巧。C++ stringstream 类是一种十分有用的类,特别是当我们需要在程序中使用字符串和数字数据互相转换的时候。
要想在程序中使用 stringstream 类,我们需要在源程序文件中包含头文件include。
stringstream 对象的使用方法与 cout 对象和 cin 的使用方法基本相同。>> 这个符号就很形象,比如:
cin>>a 可以理解为将数据流流入到 a 中
cout< 可能对于底层的描述不太恰当,但是大家记住 >> 指向谁,则是将数据给到谁,stringstream 当成 cin cout 用即可。在我上面给出的代码中,大家可以看到我还将数据还进行了转化处理,在 C++ 中数据类型的转化使用 stringstream 也是不错的选择。
这道题是前缀和的变种,变种的方式可以通过模拟解决,时间复杂度为 O(N2),但是题目的范围为 1e4,不超时方法可行。
题意:
国王将金币作为工资,发放给忠诚的骑士。
第一天,骑士收到一枚金币;
之后两天(第二天和第三天),每天收到两枚金币;
之后三天(第四、五、六天),每天收到三枚金币;
之后四天(第七、八、九、十天),每天收到四枚金币......;
这种工资发放模式会一直这样延续下去:当连续 N 天每天收到 N 枚金币后,骑士会在之后的连续 N+1 天里,每天收到 N+1 枚金币。
请计算在前 K 天里,骑士一共获得了多少金币。
输入描述:
输入只有 1 行,包含一个正整数 K (1≤K≤104),表示发放金币的天数。
输入样例:
6
输出描述:
输出只有 1 行,包含一个正整数,即骑士收到的金币数。
输出样例:
1000
#include
#include
#include
using namespace std;
int comp(int n)
{
int sum = 0;
int day = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < i; j++)
{
sum += i;
day += 1;
if (day == n)
return sum;
}
}
return sum;
}
int main()
{
int n;
cin>>n;
int ans=comp(n);
cout<<ans;
}
这个题目获得最大利润的方式,就是存在 A,B 两天,A 在 B 前一天,使得 B—A 的值最大。
题意:
实现一个算法寻找最大化股票交易利润的策略。介绍如下:
- 股票价格每天都在变化,以数组的索引表示交易日,以数组的元素表示每天的股票价格。
- 可以通过买入和卖出获得利润。一天只能进行一次买入或卖出操作,一次买入加卖出操作称为一次交易次数。
- 你只能交易一次,求使得利润最大的交易策略。
输入描述:
第一行为数字 N,表示共有 N 天。
第二行为 N 个数字 Ai,表示每天的股票价格。
其中,1≤N,Ai≤1e4。
输入样例:
8
2 5 6 1 4 3 1 3
输出描述:
输出一行,为交易一次的最大利润(有可能利润为负)。
输出样例:
4
#include
#include
using namespace std;
int main()
{
int a[100005];
int n;
cin>>n;
int mini=-0x3f3f3f3f;//一个常用的极小值
for(int i=0; i<n; i++)
{
cin>>a[i];
}
for(int i=0; i<n-1; i++)
{
for(int j=i+1; j<n; j++)
{
int t=a[j]-a[i];
if(mini<t)
{
mini=t;
}
}
}
cout<<mini;
return 0;
}
题意:
题目描述
在很久很久以前,有 n 个部落居住在平原上,依次编号为 1 到 n。第 i 个部落的人数为 ti。
有一年发生了灾荒。年轻的政治家小蓝想要说服所有部落一同应对灾荒,他能通过谈判来说服部落进行联合。
每次谈判,小蓝只能邀请两个部落参加,花费的金币数量为两个部落的人数之和,谈判的效果是两个部落联合成一个部落(人数为原来两个部落的人数之和)。
输入的第一行包含一个整数 nnn,表示部落的数量。
第二行包含 nnn 个正整数,依次表示每个部落的人数。
其中,1≤n≤1000,1≤ti≤1e4。
输出一个整数,表示最小花费。
示例 1
输入
4
9 1 3 5
输出
31
这题是一个贪心问题,要想使得花费金额之和最小,就要使得每一次的花费尽可能少。
根据题目我们可以得到,合成的新部落的花费=人数为原来两个部落的人数之和。
如何使得等待时间最小的呢?
如果这么考虑这道题是做不出来的,应该考虑的是使得每位同学等待的时间和最小。我们回忆一下答疑拿到题目:
第一位同学等待时间 S1=T1=0 -----(1)
第二位同学等待时间 S2=T1+T2=T2 -----(2)
第三位同学等待时间 S3=T1+T2+T3=T2+T3 -----(3)
......
第 N 位同学等待时间 Sn=T1+T2+T3+T4+T5+...+Tn-1 -----(n)
将 1 到 n-1 式带入 n 式得
Sn=T1*n+T2\*(n-1)+T3\*(n-1)+....+Tn
由此可知前面的系数是最大的,所以要使前面的时间最小。
于是得出了贪心策略进而解决问题。
此时贪心的除了结论是每位同学的等待时间(前一位同学的等待时间+前一位同学的进门时间+答疑时间+前一位同学的收拾东西的时间)最小。
一组样例:
4
3 4 5 6
如果按照顺序组合的话:
3+4=7
7 5 6
7+5=14
14 6
14+6=20
最终花费:7+14+20=41
其实答案应该是:
3 4 5 6
3+4=7
7 5 6
再次排序
5 6 7
5+6=11
11 7
11+7=18
最终花费为 7+11+18=36
所以这里贪心原则是维护最小的值,但是每次都会进行更新,每次更新后就要重新排序,时间复杂度是 Nlog(n) 的复杂度,这里是可以通过的,当然我们也可以使用优先队列解题。
优先队列的使用方式跟队列是一模一样的,优先队列会自动进行排序而已。
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出(first in, largest out)的行为特征。通常采用堆数据结构来实现。
简单的多次排序做法:
#include
#include
#include
using namespace std;
int main()
{
int n;
cin>>n;
int cnt=0,k;
vector<int> t;
for(int i=0; i<n; i++)
{
cin>>k;
t.push_back(k);
}
while(t.size()>1)
{
//排序
sort(t.begin(),t.end());
//取出前两个值
int k=t[0]+t[1];
//答案求和
cnt+=k;
//删除前两个利用过的值
t.erase(t.begin());
t.erase(t.begin());
//将产生的新值加入集合
t.push_back(k);
}
cout<<cnt<<endl;
return 0;
}
优先队列做法:
#include
#include
using namespace std;
int main()
{
int n,cnt=0;
priority_queue<int>pq; //默认是大顶堆,从大到小排序
cin>>n;
for(int i=0; i<n; ++i)
{
int a;
cin>>a;
pq.push(-a); //存入负值,从小大排序
}
if(pq.size()==1)
{
cout<<-pq.top();
}
else
{
while(pq.size()!=1)
{
int x=pq.top();
pq.pop();
int y=pq.top();
pq.pop();
pq.push(x+y);
cnt+=-x-y;
}
cout<<cnt<<endl;
}
return 0;
}
首先要包含头文件 #include,他和 queue 不同的就在于我们可以自定义其中数据的优先级,让优先级高的排在队列前面,优先出队。
优先队列具有队列的所有特性,包括队列的基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
题意:
上课的时候总有一些同学和前后左右的人交头接耳,这是令小学班主任十分头疼的一件事情。
不过,班主任小雪发现了一些有趣的现象,当同学们的座次确定下来之后,只有有限的 D 对同学上课时会交头接耳。
同学们在教室中坐成了 M 行 N 列,坐在第 i 行第 j 列的同学的位置是(i,j),为了方便同学们进出,在教室中设置了 K 条横向的通道,L 条纵向的通道。
于是,聪明的小雪想到了一个办法,或许可以减少上课时学生交头接耳的问题:她打算重新摆放桌椅,改变同学们桌椅间通道的位置,因为如果一条通道隔开了两个会交头接耳的同学,那么他们就不会交头接耳了。
请你帮忙给小雪编写一个程序,给出最好的通道划分方案。在该方案下,上课时交头接耳的学生对数最少。
输入:
输入第一行,有 5 各用空格隔开的整数,分别是 M,N,K,L,D(2≤N,M≤1000,0≤K<M,0≤L<N,D≤2000)。
接下来 D 行,每行有 4 个用空格隔开的整数,第 i 行的 4 个整数 Xi,Yi,Pi,Qi,表示坐在位置 (Xi,Yi)与 (Pi,Qi) 的两个同学会交头接耳(输入保证他们前后相邻或者左右相邻)。
输入数据保证最优方案的唯一性。
输出:
输出共两行。
第一行包含 K 个整数,a1,a2,⋯aK,表示第 a1a_1a1 行和 a1+1a_1+1a1+1 行之间、第 a2 行和第 a2+1行之间、…、第 aK 行和第 aK+1 行之间要开辟通道,其中 ai<ai+1,每两个整数之间用空格隔开(行尾没有空格)。
第二行包含 L 个整数,b1,b2,⋯bk,表示第 b1 列和 b1+1 列之间、第 b2 列和第 b2+1 列之间、…、第 bL 列和第 bL+1 列之间要开辟通道,其中 bi<bi+1,每两个整数之间用空格隔开(行尾没有空格)。
示例 1
输入
4 5 1 2 3
4 2 4 3
2 3 3 3
2 5 2 4
输出
2
2 4
所以输出需划分的通道时,要先将通道按编号由小到大排序后再输出 。
#include
#include
#include
using namespace std;
const int MAXN= 1001;
int x[MAXN]; //横坐标桶
int y[MAXN]; //纵坐标桶
int c[MAXN];
int o[MAXN];
int main()
{
int M, N, K, L, D;
cin >> M >> N >> K >> L >> D;
int xi, yi, pi, qi;
while (D--)
{
cin >> xi >> yi >> pi >> qi;
if (xi == pi) //横坐标相同
{
y[min(yi, qi)]++;
}
else //纵坐标相同
{
x[min(xi, pi)]++;
}
}
//两重循环找出前K大的横坐标值
for (int i = 1; i <= K; i++)
{
int maxn = -1;
int p;
for (int j = 1; j < M; j++)
{
if (x[j]>maxn)
{
maxn = x[j];
p = j;
}
}
x[p] = 0;
c[p]++;
}
//两重循环找出前L大的横坐标值
for (int i = 1; i <= L; i++)
{
int maxn = -1;
int p;
for (int j = 1; j < N; j++)
{
if (y[j]>maxn)
{
maxn = y[j];
p = j;
}
}
y[p] = 0;
o[p]++;
}
for (int i = 0; i<MAXN; i++)
{
if (c[i])
printf("%d ", i);
}
printf("\n");
for (int i = 0; i<MAXN; i++)
{
if (o[i])
printf("%d ", i);
}
return 0;
}
可见在真正的竞赛中,像这样各种知识的混着出题,才是常见的。单一知识点出题,我们称作签到题,就是所有人都会做的。当然在蓝桥杯省赛中,能够将前面的知识学会学好,省二是没有问题,想要冲击更高的奖项,基础篇只是打好了基础,后面的课程会带你认识更多的算法,体验算法之美。
原网址:https://www.lanqiao.cn/courses/3993/learning/?id=248906