某个店被抢劫了, 只记得所有的商品是从x开始标记,然后有y个,现在只知道剩下的商品的标号,求最少被偷了多少商品。
很简单了,排序,然后从头到位遍历一边,看中间缺了多少,就加多少就行
n = int(input())
keyboards = map(int, input().split())
keyboards = sorted(keyboards)
#print(keyboards)
last = keyboards[0]
res = 0
for i in keyboards[1:]:
if last + 1 != i:
res += i - last - 1
last = i
print(res)
已知家里的墙大小为a和b,想要买比例为x:y的电视。商场里的电视尺寸可以为任意大小。求问可能存在多少能放在家里的墙上,并且比例为x:y的电视。
求出xy的gcd,然后把x和y分别除以gcd,得到最小的电视尺寸minX,minY。剩下的就是看墙能放下多少minX或者minY了。取a/minX和b/minY里面较小的整数即可(大的那个会导致另一个方向超长)
import math
[a,b,x,y] = map(int, input().split())
gcd = math.gcd(x,y)
minX = x / gcd
minY = y / gcd
counta = int(a//minX)
countb = int(b//minY)
print(min(counta, countb))
这个人每天都工作m分钟。然后他很喜欢喝咖啡,每次喝咖啡消耗1分钟。但是老板不喜欢他摸鱼,所以他至少要隔d分钟才能喝一次咖啡。然后这个人很矫情,想要在n个不同的分钟里面喝咖啡。。。嗯。。。太尼玛奇葩了。。。求需要多少天,才能把这n个喝咖啡的时间都体验过。
相信看了我中文描述,还是有人不懂这个题目到底说啥。。。所以我当时就没看懂这个题目,果断跳过了。
后来比赛期间有两次announcement,都是针对这个题目的,包括,1,不用考虑休息日,2,n个分钟都是独立的。
哦,所以题目是希望我们把n个分钟数,放到k个工作日里面,要求k尽可能小,同时满足每次喝咖啡间隔d分钟的条件即可。注意每天开始的时间都重算,不用等d分钟再开始喝咖啡,可以一开始就喝一次。
用贪心算法,把所有的咖啡时间都排序,然后从头到尾遍历,每天开始,先拿队列里第一个没有喝过咖啡的时间a,然后把这个时间加d,找到第一个大于a+d的喝咖啡时间,直到所有时间遍历完。
注意,由于测试用例为10e5级别,所以不能做成O(n^2)的时间复杂度,必须在O(nlogn)时间复杂度内解决战斗。所以,我们在取时间的时候需要有技巧
1,使用一个map来存喝咖啡的时间和他对应的索引i,以喝咖啡时间为key,索引i为value,就可以实现根据时间排序的效果了。
2,每次使用了某个时间,直接将该时间从map里面移除。
3,已经定好了本次喝咖啡的时间,查找下次喝咖啡的时间,不能用遍历法,必须用二分查找来寻找。遍历法会在最坏情况下降到O(n^2)时间复杂度。
#include
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
#define TESTINGx
#ifdef TESTING
#define DEBUG(S, args...) {printf(S, args);printf("\n");}
#else
#define DEBUG(S, args...)
#endif
int cmp(const void* a,const void* b)
{
int a1 = *(int*)a;
int b1 = *(int*)b;
return a1 - b1;
}
int main()
{
int n,m,d;
cin >> n >> m >> d;
map index;
int x;
set times;
int ans[200005];
for (int i = 0; i < n; ++i) {
scanf("%d", &x);
index[x] = i;
}
int day = 1;
while(!index.empty())
{
//begin of a day, drink one coffe :)
auto it = index.begin();
ans[it->second] = day;
int last = it->first;
DEBUG("last is :%d", *it);
index.erase(it);
while((it = index.upper_bound(last + d)) !=index.end() ) {
ans[it->second] = day;
last = it->first;
index.erase(it);
}
day++;
}
printf("%d\n", day - 1);
for (int i = 0; i < n; ++i) {
printf("%d ", ans[i]);
}
printf("\n");
return 0;
}
题目给出n个风区,你可以从任何高为h的地方开始滑翔,没有风区的时候,按每秒下降1个单位下降,在风区内,则保持水平滑翔。问,从哪里飞出能飞最远的水平距离。
第一眼就觉得像动态规划。于是仔细分析,还真是。
我们首先直到,最优的解法,肯定是从某个风区的开始处出发。这个简单思考就能得到。在风区中间,肯定是浪费的。在非风区中间,即使能飞最远距离,也肯定是等效于从风区出发的,因为非风区飞行的距离是固定的,总是h。
为了避免重复计算,我们需要记录三个数据。
dp[i][0],从第i个风区出发,可以飞行多少距离
dp[i][1],从第i个风区出发,到达最后一个风区的时候,会从什么高度出来。
dp[i][2],从第i个风区出发,最后经过的风区是哪个。
记录了这些内容之后,我们就可以从最后一个风区开始,往前计算每个风区飞行的距离了。
1,初始化dp[n][0] = h+distance[n], dp[n][1] = h, dp[n][2] = n
2, 初始化dp[i][0] = distance[i+1] + distance[i],dp[i][1] = dp[i+1][1]- distance_between[i][i+1],dp[i][2] = dp[i+1][2]
3,如果dp[i][1] 小于等于0,说明最后一个风区不能到达(已经撞到地上去了),需要往前移动。从dp[i][2]开始,不断往前,每次递减dp[i][2],然后dp[i][2]递减风区distance_between[ dp[i][2] ] [dp[i][2] - 1 ],dp[i][0]则递减 distance[dp[i][2]
每次都尝试用dp[i][0]的值更新答案即可
#include
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
#define TESTINGx
#ifdef TESTING
#define DEBUG(S, args...) {printf(S, args);printf("\n");}
#else
#define DEBUG(S, args...)
#endif
int main()
{
int n, h;
int x[200005][2];
ll dp[200005][3];
cin >> n >> h;
for (int i = 1; i <= n; ++i) {
scanf("%d %d", &x[i][0], &x[i][1]);
}
dp[n][0] = h + (x[n][1] - x[n][0]);
dp[n][1] = h;
dp[n][2] = n;
DEBUG("dp[%d][0]:%lld, dp[%d][1]:%lld, dp[%d][2]:%lld", n,dp[n][0],n,dp[n][1],n,dp[n][2]);
ll ans = dp[n][0];
for(int i = n - 1; i > 0; --i)
{
//delta
int delta = x[i+1][0] - x[i][1];
DEBUG("i:%d, delta %d", i, delta);
dp[i][0] = dp[i+1][0] + (x[i][1] - x[i][0]);
dp[i][1] = dp[i+1][1] - delta;
dp[i][2] = dp[i+1][2];
while(dp[i][1] <= 0 && dp[i][2] >= i)
{
DEBUG("moving dp[%d][0]:%lld, dp[%d][1]:%lld, dp[%d][2]:%lld", i,dp[i][0],i,dp[i][1],i,dp[i][2]);
int curWind = dp[i][2];
dp[i][0] -= x[curWind][1] - x[curWind][0];
dp[i][1] += x[curWind][0] - x[curWind - 1][1];
dp[i][2]--;
}
ans = std::max(ans, dp[i][0]);
DEBUG("dp[%d][0]:%lld, dp[%d][1]:%lld, dp[%d][2]:%lld", i,dp[i][0],i,dp[i][1],i,dp[i][2]);
}
printf("%lld", ans);
return 0;
}
有一棵树,其节点从1到n顺序标记。输入是将每一条边分别去除后,分成两个子图,每个子图里面的点的最大标记数。求问是否可以根据输入构造一个树。然后求这个树。
图论表示不熟,看答案的。
需要构造成树很简单,
1,由于输入规定每次输入的 a<=b总是成立,所以我们需要判定,b总是为n,否则就是不成立的。试想,不管去掉哪个边,总有一个子图里面有第n个节点嘛。这个好说。
2,对于所有a,按从小到大排列。然后,对于任意一个ai, 总有 i > ai。这是因为,对于任意索引k,你最多只能构造k个子树,使得k总是最大节点。这个推理需要递归地往下推,这就得到了i > ai的条件了。
然后,关于怎么构造的问题。事实上,由于没有附带条件,你可以直接构造一个所有节点都只有一个儿子节点的bamboo。然后根节点是n,叶子节点是a1。
构造过程如下:
1,先把a排序,从小到大排
2,对于任意一个i,如果ai < ai+1成立,我们把ai+1用上。
3,否则,我们从没有用过的节点里面找一个,最小的。
最后,把n接上,就ok了。
#include
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
#define MAXN 1005//2e5
#define TESTINGx
#ifdef TESTING
#define DEBUG(S, args...) {printf(S, args);printf("\n");}
#else
#define DEBUG(S, args...)
#endif
int cmp(const void*a, const void* b)
{
int ia = *(int*)a;
int ib = *(int*)b;
return ia - ib;
}
int main()
{
int n,a,b;
int input[MAXN];
cin >> n;
for (int i = 1; i < n; ++i) {
scanf("%d %d", &a, &b);
input[i] = a;
if(b != n)
{
printf("NO\n");
return 0;
}
}
qsort(input, n, sizeof(int), cmp);
for(int i = 1; i < n; ++i)
{
DEBUG("input[%d] %d", i,input[i])
if(i > input[i])
{
printf("NO\n");
return 0;
}
}
vector ans;
set unused;
for (int i = 1; i < n; ++i) {
unused.insert(i);
}
ans.push_back(input[1]);
unused.erase(input[1]);
for(int i = 2; i
有一个地铁的管道,管道上下两边装了很多感应器。我们的任务是,从管道的上下分别选一个点,作为激光的入射路径,使得激光能尽可能多的射在感应器上面。激光遇到管道的墙壁总是做镜面反射,可以把管道内壁认为是一面光滑的镜子。感应器不影响激光的反射和前进。
首先,管道有多高,是没有用的。我们只需要关心x的距离,选择合适的dx,然后计算能照射到多少感应器即可。
其次,dx的选择,实际上最优选择总是2的指数次(包括0次)或者0。嗯。。。这里感觉就被大神们智商压制了,想了好久才明白这个是为什么。
我们先看怎么计算照射到的感应器。
对于任意dx,以及启动位置Ax,我们可以知道,从发射的那面管道,我们能覆盖的点是Ax + 2dx, Ax + 4x, Ax + 6x ..... Ax + 2k dx
然后对于另一面,我们能覆盖到的点是Ax + 3dx, Ax + 5dx, Ax+7dx ......Ax + (2k + 1)dx
ok,可以看出,对于第一个序列,如果dx为大于1的奇数,设dx=pd'x,则第一面的队列就变成了Ax + 2pd'x , Ax + 4pd'x ... Ax + 2kpd'x,这个队列明显是Ax + 2dx, Ax + 4x, Ax + 6x ..... Ax + 2k dx的子队列。所以没有必要额外枚举一次。同时,另一边,则有 Ax+3pd'x, Ax + 5pd'x, Ax + 7pd'x ... Ax + (2k +1)pd'x,由于p为奇数,所以这个队列肯定也是Ax+3d'x, Ax + 5d'x, Ax + 7d'x ... Ax + (2k +1)d'x的子队列。故而也没有必要枚举一次。(如果想不明白,可以随便找个数字带进去,然后用dx=1比较一下)
至此,我们可以看到,奇数的dx(除了1)以外,都是不需要枚举的。那么什么是2的指数次呢?实际上,针对Ax + 2dx, Ax + 4x, Ax + 6x ..... Ax + 2k dx这个数列,枚举0,1,2就足够覆盖所有case了。但是对于Ax + 3dx, Ax + 5dx, Ax+7dx ......Ax + (2k + 1)dx这个队列,则不然。这是由于他们的起始dx为奇数导致的。
可以看到,如果dx取2,得到的队列是
6 10 14 18 。。。。
如果dx取4,得到的队列就是
12 20 28 。。。。。
2不能覆盖4的情况。
所以只需要枚举log(10^9)种dx即可。每种dx,都要照对应的Ax,这个只需要针对每隔位置的坐标x进行(激光出发行)X mod (2dx)或者(非激光出发行) X + d mod (2dx),得到的结果,就是需要取多少Ax才能照射到。遍历所有的镭射,用这个模运算的结果做key,累加答案,然后遍历所有模结果,更新答案即可。
#include
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
#define MAXN 100005 //2e5
#define TESTING
#ifdef TESTING
#define DEBUG(S, args...) {printf(S, args);printf("\n");}
#else
#define DEBUG(S, args...)
#endif
int n[2];
ll input[2][MAXN];
int ans = 1;
void solve()
{
//check zero
int k = 0;
for(int i = 0;i < n[0]; i++)
{
while(input[1][k] < input[0][i])
{
k++;
}
if(input[1][k] == input[0][i])
{
DEBUG("hit:%d, %d", k, i)
ans = 2;
break;
}
}
//check for 1 and 2
for(ll i = 1;i <= 10e9; i *= 2 )
{
ull mod = i * 2;
map res;
for(int j = 0; j < 2; j++)
{
for(int l = 0; l < n[j]; l++)
{
res[(input[j][l] + j * i) % mod]++;
}
}
for(auto it:res)
{
DEBUG("i %d, first %d,second %d", i, it.first, it.second)
ans = max(ans,it.second);
}
}
}
int main()
{
int y;
cin >> n[0] >> y;
for (int i = 0; i < n[0]; ++i) {
scanf("%lld", &input[0][i]);
}
cin >> n[1] >> y;
for (int i = 0; i < n[1]; ++i) {
scanf("%lld", &input[1][i]);
}
solve();
printf("%d", ans);
return 0;
}