第六周
第一题
题目1
有N位武将,两个武将之间有一个“默契值”,小涵和计算机要轮流从自由武将中挑选武将组成自己的军队。小涵先选,计算机始终会选择于小涵的武将默契值最高的未被选择的武将。选择结束后,小涵和计算机选出默契值最高的两个武将,默契值高的获胜。问小涵是否能获胜,并且求小涵能得到的最大的默契值。
思路1
小涵第一次选择第二高默契值最高的武将,让计算机选走第一高默契值的武将,自己再选第二高默契值的武将并如此循环。这样,默契值最高的组合都会被拆散,二默契值第二高的组合在小涵手上,计算机永远会输。此时最大的默契值就是第一次和第二次小涵选的武将组合之间的默契值。
代码1
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
//存储所有武将之间的默契值
int g[501][501];
int n;
cin >> n;
for (int i = 1; i < n; i++)
{
for (int j = i + 1; j <= n; j++)
{
cin >> g[i][j];
g[j][i] = g[i][j];
}
}
//最大答案,初始值给一个足够小的数
int ans = -2e9;
//遍历所有武将
for (int i = 1; i <= n; i++)
{
//将这个武将的默契值排序
sort(g[i] + 1, g[i] + 1 + n);
//n-1处是第二高默契值。找最大的第二高默契值
if (g[i][n - 1] > ans)
{
ans = g[i][n - 1];
}
}
//输出1必赢
cout << "1" << endl << ans << endl;
return 0;
}
第二题
题目2
有一个独木桥长度为L,有一群士兵在独木桥上,朝向未知,速度为1。一个士兵某一时刻来到了坐标为0或L+1的位置,他就离开了独木桥。如果两个士兵面对面相遇,就分别转身,继续行走。求所有士兵下桥的最长时间和最短时间。
思路2
在宏观上,两个士兵相遇后转身,和两个士兵相互穿过方向不变是一样的。因此不需要考虑转身的情况。则此时最长的下桥时间就是所有士兵面向距离自己远的一端行走的距离的最大值;最短的下桥时间就是所有士兵面向距离自己近的一端行走的距离的最大值。
代码2
#include<iostream>
using namespace std;
int main()
{
//长度、n
int l, n;
cin >> l >> n;
//所有士兵面向距离自己远的一端行走的距离的最大值、所有士兵面向距离自己近的一端行走的距离的最大值
int largel = 0, smalll = 0;
//求最大值
for (int i = 1; i <= n; i++)
{
int p;
cin >> p;
int maxl = max(p, l + 1 - p);
int minl = min(p, l + 1 - p);
if (largel < maxl)
{
largel = maxl;
}
if (smalll < minl)
{
smalll = minl;
}
}
cout << smalll << " " << largel << endl;
return 0;
}
第三题
题目3
有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,求n个人的平均等待时间的最小值。
思路3
简单的贪心。让接水时间短的先接。
需要按照结构体的某个变量来排序结构体,可以用sort函数+Lambda表达式。
Lambda表达式:一个匿名函数
Lambda表达式:当需要将函数作为参数时,不必单独写一个有名的函数
Lambda表达式的语法:
捕获返回值(可省)
{函数体}
这里不需要使用捕获,为空即可
代码3
#include<iostream>
#include<iomanip>
#include<algorithm>
using namespace std;
//每个准备接水的人
struct Person
{
//这个人需要的时间
int time;
//这个人的序号(因为后面需要输出)
int index;
};
//所有的人
Person p[1000];
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
p[i].index = i + 1;
cin >> p[i].time;
}
//按接水时间
//lambda表达式:
//参数:两个Person参数,left和right
//这里需要一个用来判断大小的函数,所以参数就是两个需要比较大小的数,sort函数用这个函数判断这里的left是不是比right小
//函数体的实现是用time变量来比较这两个值
sort(p, p + n, [](Person left, Person right) { return left.time < right.time; });
//总等待时长
double sum = 0;
//排序后,在前的人耗时短
for (int i = 0; i < n; i++)
{
//输出序号顺序
cout << p[i].index << " ";
sum += (n - 1 - i) * p[i].time;
}
cout << endl;
cout << setiosflags(ios::fixed) << setprecision(2) << sum / n << endl;
return 0;
}
第四题
题目4
有很多堆果子,需要将其合并,一次合并两堆。每次合并需要消耗两堆果子数量总和的体力。问需要的最少体力是多少。
思路4
先把数量少的果堆合并,直到合并完成。如果先合并数量多的,这堆合并好的果子在下次被合并时会消耗更多体力。
由于果堆的数量在一直变少,并且又要求能一直找到数量最少的果堆,则可以用优先队列来保证数据有序。
代码4
#include<iostream>
#include<queue>
using namespace std;
int main()
{
//优先队列。因为需要小的保持在队列前面,应该是从大到小排序,用greater<int>
priority_queue<int, vector<int>, greater<int> > q;
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
int t;
cin >> t;
//入队列
q.push(t);
}
//需要的体力
int ans = 0;
//队列只剩1个元素时,就代表只剩一堆果子,也就是合并完成
while (q.size() > 1)
{
//sum存放数量最小的两个果堆的数量和
//top是当前最小的的数
int sum = q.top();
q.pop();
sum += q.top();
q.pop();
//累加体力
ans += sum;
//把新果堆放入队列
q.push(sum);
}
cout << ans << endl;
return 0;
}