2022-01-15 每日打卡:难题精刷
写在前面
“这些事儿在熟练之后,也许就像喝口水一样平淡,但却能给初学者带来巨大的快乐,我一直觉得,能否始终保持如初学者般的热情、专注,决定了在做某件事时能走多远,能做多好。” 该系列文章由python编写,所刷题目共三个来源:之前没做出来的 ;Leetcode中等,困难难度题目; 周赛题目;某个专题的经典题目,所有代码已AC。每日1-3道,随缘剖析,希望风雨无阻,作为勉励自己坚持刷题的记录。
同时运行 N 台电脑的最长时间
今天又打了一场周赛,最后一题超时了😥这道题,考完看题解,后知后觉己前几天做过类似的,就是Aggressive cows那道,总结以下:
最优排布问题一般有这么几个思路:
- 动态规划。比如今天的第三题解决智力问题,出的很快哈哈哈。一看有边界条件,有转移方程(很可能是针对组合/转移的分类讨论)。
- 贪心。往往是正解,比如这个题,我每次只使用单位1的电,从前n个最大剩余的电池中选就可以。
- 枚举。 往往是直接寻找最优安排方案超出时间or算法困难。尝试变换角度为控制变量大小,判断是否满足条件! 比如这个题中将“在现有条件的基础上给定天数,判断是否能够维持这些电脑同时运行。”,太妙啦。
我【超时】的解答:
class Solution:
def maxRunTime(self, n: int, batteries: List[int]) -> int:
ans = 0
while(batteries.count(0)<=len(batteries)-n):
batteries.sort(reverse = True)
for i in range(n):
batteries[i]-=1
ans+=1
return ans
榜一的贪心思路:
首先计算出的 平均值 是时间的上界,因为 存在电池因为不能同时使用而不能使用全部电量
- 超过平均值的,那些电池给一个电脑从头用到尾,所以不考虑这个电脑 也不考虑这个电池。
- 直到最大的都不够平均值,那么答案就是剩下的所有电池混用能够维持的最大时间。
为什么这样是正确的呢?借用一个图,相当于用不同的条来填满矩形,几台电脑满足同一行的颜色不同(同一时间不能使用同一块电池)。
可以发现,在填充的时候对于大于平均值的一定能满足无论最后ans取几的情况,且超过ans部分怎样都无法排布(试想一下上图的红色,超过ans的部分还有1,排布在哪一行都会发生冲突)。
而对于小于平均值的,我们可以选择任何一行放(此时有 batteries[i] <= avg = len(rows)
),所以一定可以全部安置下!
枚举+二分的写法:
主要就是要找到上面的check函数的条件为n*x = sum( min(b, x) for b in batteries )
,原因在于
- rows不断变小的过程,原先可选择的行的位置变小,相当于每一行可以选择的条变多了,一定仍然满足;
- 而rows不断变大的过程,要填满的矩阵也在不断变大,可选择的条数“变少”,可能无法满足条件。
就这样使用二分法不断逼近即可。
class Solution:
def maxRunTime(self, n: int, batteries: List[int]) -> int:
tot = sum(batteries)
l, r = 1, tot // n + 1
while l < r:
x = (l + r) // 2
if n * x <= sum(min(b, x) for b in batteries):
l = x + 1
else:
r = x
return r - 1
奶酪
dfs:给自己提个醒,对于这种多输入多输出的问题,每次循环调用一个案例一定记得把变量,数组什么的都还原。
#include <stack>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
const int N = 501, M = 1e4 + 1;
struct Hole
{
int x, y, z;
bool flag;
double operator-(const Hole &other)
{
return pow((this->x - other.x), 2) + pow((this->y - other.y), 2) + pow((this->z - other.z), 2);
}
friend ostream &operator<<( ostream &output,const Hole &D )
{
output<<D.x << D.y << D.z;
return output;
}
} holes[M];
int len;
int cnt;
int n, h, r;
stack<Hole> stk;
Hole tmp;
bool ans = false;
stack<Hole> s2;
void func()
{
scanf("%d %d %d", &n, &h, &r);
cnt = n;
for (int i = 0; i < cnt; i++)
{
scanf("%d %d %d", &holes[i].x, &holes[i].y, &holes[i].z);
if (holes[i].z <= r)
{
stk.push(holes[i]);
holes[i].flag = false;
}else{
holes[i].flag = true;
}
}
while (stk.size())
{
tmp = stk.top();
stk.pop();
if (tmp.z + r >= h)
{
ans = true;
break;
}
for (int i = 0; i < cnt; i++)
{
if (holes[i] - tmp <= pow((2 * r),2) && holes[i].flag)
{
stk.push(holes[i]);
holes[i].flag = false;
}
}
}
if (ans)
{
printf("Yes");
}
else
{
printf("No");
}
printf("\n");
}
int main()
{
scanf("%d", &len);
for (int i = 0; i < len; i++)
{
memset(holes, -1, sizeof holes);
stk = stack<Hole>();
ans = false;
func();
}
}
拓扑+并查集的做法也是可以的,但是这个oj对python的输入要求很不友好,没有再写。