作者:老陈
大家都知道Steam这款理财软件吧?它是当前最大的游戏发布平台之一。玩家可以在该平台购买、下载、讨论、上传和分享游戏和软件。
老陈最近安利了$N$个朋友来使用Steam,他们有一部分人在Steam上购买了或者退购了某款游戏。
现在老陈知道他们在这段时间内购买/退购的情况 ,他想请你告诉他他的某些朋友拥有了几个游戏。
输入三个正整数$N$,$M$,$Q$,分别代表被老陈安利来使用Steam的人数,这些人在Steam上的购买/退购记录条数,老陈想知道的朋友的游戏个数的人数。($1\leq N\leq 10^5$,$1\leq M \leq 10^6$,$1\leq Q \leq 10^5$)
接下来输入$M$行,每行代表一个购买记录。格式为:
记录类型 购买/退购人 游戏名
记录类型仅包含一个字母:“G”为购买游戏,“T”为退购游戏。购买/退购人和游戏名均为一个长度不大于20且不包含空格的字符串。保证退购的游戏在之前肯定购买且游戏不会重复购买,记录按时间顺序给出。
接下来$Q$行,每行代表一个询问。
每行包含一个长度不超过20的字符串,代表老陈询问当前游戏个数的朋友名字。
对于每个询问,输出一个整数,代表这位朋友在Steam上拥有的游戏个数。
3 6 3
G LiMing SHENZHEN_I/O
G ZhangSan SUDOKU
G LiSi CROSS_FIRE
G LiMing CROSS_FIRE
T LiSi CROSS_FIRE
G LiMing SUDOKU
LiSi
ZhangSan
LiMing
0
1
3
对于A题:
可以使用map来实现存储,对于每个操作,用户游戏数仅会+1或-1。(保证退购的游戏在之前肯定购买且游戏不会重复购买)。
对于B题:
hash一下字符串就行了。(其实A的想法放到B题可以卡过去)
1 |
|
在C语言中,“a”加上一个数的结果是一个整数。
例如:“a”+6=97+6=103
但是老陈觉得这样很无聊,所以他改变了这个加的定义:加N的结果是当前字符串按字典序的顺序之后第N个字符串。
例如:”az”+4的结果就是”az”之后第四个字符串。(“az”,”ba”,”bb”,”bc”,”bd”),所以”az”+4=”bd”。
现在请你编写程序实现它。
第一行包含一个正整数$T$,代表数据组数。($1\leq T \leq 1000$)
接下来$T$行,每行一个测试数据。每个测试数据包含一个字符串$s$和一个正整数$X$。(字符串长度不大于$10^4$且保证非空,$1\leq X \leq 10^{13}$)
对于每组测试数据,输出$s+X$的结果。
3
a 10
abcd 5
zzz 3
k
abci
aaac
将字符串看成26进制数,将数字$X$转化为26进制数后相加即可。相加之后注意进位,按字母输出即可。
1 |
|
老陈非常喜欢素数,他现在想从某个区间的数中挑出任意个数相乘得到一个新数。如果这个新数是素数,老陈的兴奋值会+1。
一开始老陈的兴奋值为0,之后他会选择N个区间,对于每个区间他会进行K次操作,当然这K次操作不会存在重复,这样老陈才会真的开心。
请问最后老陈的兴奋值的最大值是多少?
第一行输入两个数$N$和$K$。($1 \leq N \leq 10^6$,$1\leq K \leq 10^9$)
接下来$N$行,每行输入两个整数l和r代表老陈进行操作的区间 $[l,r]$。($1\leq l \leq r \leq 10^6$)
输出一个整数代表老陈操作结束后兴奋值的最大值。
2 5
2 10
1000 5000
0
回想一下素数的定义:因子只有1和它自己的数。那么对于一个素数$p$,肯定只有一种乘法表示:$p=1*p$ 。而对于其他的数,相乘的结果肯定不是素数。
那么,对于没有包含1的区间,对答案的贡献肯定是0。而对于包含1的区间,对答案的贡献为区间内的素数个数。
素数筛+前缀和即可解决问题。
1 |
|
老陈某天的心情值跟过山车一样跌宕起伏,平常的时候,老陈的心情值是不下降的(即可能升高或不变但不会下降)。但是当老陈受到打击的时候(做题自闭什么的),心情值就是不上升的(即可能下降或不变但不会上升)。
他的朋友想去安慰(调侃)一下老陈:“你在$a$时间到$b$时间是不是受到了什么打击了啊说给我乐乐?”
如果老陈在这段时间内的心情值前一部分是不下降的,剩下的部分是不上升的,那么他的朋友才会觉得这个提问有价值。所以请你判断一下这些提问时间是不是有价值的。
第一行包含两个整数$n$,$m$。分别代表这一天的时间和他的朋友询问的次数。($1 \leq n,m \leq 10^5$)
第二行包含$n$个整数代表这一天的每一个时间老陈的心情值。(均不大于$10^9$且均为正整数)
接下来$m$行,每行包含两个正整数$a$,$b$,代表他的朋友询问的时间段。($1 \leq a \leq b \leq n $)
对于每一个时间段,如果是值得提问的,输出”Yes”;否则输出”No”。
8 6
1 2 1 3 3 5 2 1
1 3
2 3
2 4
8 8
1 4
5 8
Yes
Yes
No
Yes
No
Yes
$dp[i]$表示以$a[i]$为终点的连续不下降序列的长度,$rdp[i]$表示以$a[i]$为起点的连续不下降序列的长度。
如果$dp[l]+rdp[r]>=r-l+1$,那么就是$Yes$,否则为$No$。
1 |
|
最近老陈的亲戚家重新装修,老陈被叫去帮忙刷墙。
老陈拿着墙刷,刷着刷着感到无聊就转了一下。这转了一下不要紧,老陈的脑袋突然嗡的一声蹦出一个问题:
我拿着这个墙刷转一个角度,它涂了多大的面积的墙?
这问题一出来就不得了,老陈想了好久还没想出来。你能帮他解决吗?
我们用标准一点的语言来描述这个问题:
有一个矩形(即墙刷),左边的边长为$a$,上边的边长为$b$,后该矩形以左下角的点为旋转点进行旋转角度 $\theta$(如图所示)。求蓝色区域的面积。
输入包含三个整数$a$,$b$,$\theta$。($1\leq a,b\leq 10^6$,$0\leq \theta \leq 180$)
输出一个数代表所求面积,保留三位小数。
2 2 90
10.283
答案即为扇形面积加上区域$I$和区域$II$的面积。(即矩形面积)
1 |
|
E题若角度范围增大为360的话该怎么处理呢?
作者:肖锐
最长公共子序列不需要连续
给定序列
s1={3,5,7,4,8,6,7,8,2}
s2={1,3,4,5,6,7,7,8}
s1和s2的相同子序列,且该子序列的长度最长,即是LCS
s1和s2的其中一个最长公共子序列是 {3,4,6,7,8}
动态规划算法通常用于求解具有某种==最优性质==的问题。
在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有==最优值==的解
例如LCS中的‘最长’
数列:1、1、2、3、5、8、13、21、34、……1
2
3
4
5
6
7
8
9int f(int n)
{
if(n == 0) return 0;
if(n == 1 ) return 1;
if(n >= 2)
{
return f(n-1)+f(n-2);
}
}
这种算法并不高效,它做了很多重复计算,它的时间复杂度为$O(2^n)$
在斐波拉契数列,可以看到大量的重叠子问题,比如说在求fib(6)
的时候,fib(2)
被调用了5次。如果使用递归算法的时候会反复的求解相同的子问题,不停的调用函数,而不是生成新的子问题。
使用动态规划来将重复计算的结果具有”记忆性”,就可以将时间复杂度降低为O(n)1
2
3
4
5
6
7
8void f()
{
int f[10];
f[0] = 0;
f[1] = 1;
for(int i = 2; i <= 10; i++)
f[i] = f[i-1] + f[i-2];
}
解决LCS问题,需要把原问题分解成若干个子问题,所以需要刻画LCS的特征
设S1={A0,A1, … ,Am},S2={B0,B1, … Bn},它们LCS为Z={Z1,Z2, … ,Zk}
即假如S1的最后一个元素 与S2的最后一个元素相等,那么S1和S2的LCS就等于 {S1减去最后一个元素} 与 {S2减去最后一个元素} 的 LCS 再加上 S1和S2相等的最后一个元素
设S1={A0,A1, … ,Am},S2={B0,B1, … Bn},它们LCS为Z={Z1,Z2, … ,Zk}
如果Am!=Bn
若Zk!=Bn,则{Z1,Z2, … ,Zk}是{A0,A1, … ,Am}和{B0,B1, … B(n-1)}的一个最长公共子序列
假如S1的最后一个元素 与 S2的最后一个元素不等,那么S1和S2的LCS就等于 : MAX( {S1减去最后一个元素} 与 S2 的LCS, {S2减去最后一个元素} 与 S1 的LCS)
假设我们用C[i,j]表示Xi 和 Yj 的LCS的长度(直接保存最长公共子序列的中间结果不现实,需要先借助LCS的长度)。其中X = {x1 … xm},Y ={y1…yn},Xi = {x1 … xi},Yj={y1… yj}。
可得动态转移方程如下:
s1={1,3,4,5,6,7,7,8},s2={3,5,7,4,8,6,7,8,2}
图中的空白格子需要填上相应的数字(这个数字就是C[i,j]的定义,记录的LCS的长度值)。填的规则依据公式,简单来说:如果横竖(i,j)对应的两个元素相等,该格子的值 = c[i-1,j-1] + 1。如果不等,取c[i-1,j] 和 c[i,j-1]的最大值。
首先初始化该表。
当i=2,j=1时,S1的元素3 与 S2的元素3 相等,所以 C[2,1] = C[1,0] + 1
当i=2,j=2时,S1的元素3 与 S2的元素5 不等,C[2,2] =max(C[1,2],C[2,1])
图中C[1,2] 和 C[2,1] 背景色为浅黄色。
根据性质,c[8,9] = S1 和 S2 的 LCS的长度,即为5
题意:输入不定行,每行两个字符串,求每一行两个字符串的最长公共子序列长度
1 |
|
和LCS区别是区别就是因为是连续的,如果两个元素不等,那么就要=0了而不能用之前一个状态的最大元素
假设有序列A = {5, 2, 8, 6, 3, 6, 9, 7}
其递增子序列有:{5,8,9}, {2,6,9}, {5,6,7}……
其中,最长递增子序列为{2, 3, 6, 9}和{2, 3, 6, 7}
设dp[i]表示以i结尾的子序列中LIS的长度,dp[j] (0<= j < i) 来表示在i之前的LIS的长度
有一序列A={5, 3, 4, 8, 6, 7}
d(i) = max{ 1, d(j)+1} ,且满足A[i] >= A[j]
我们所求的拦截系统的数目其实就是一个序列的所有递减子序列,并使其数量尽量减少,然后递减子序列的数目又会等于最长上升子序列中所含元素的个数;不理解的话可以去看下下面的test
1 |
|
1 |
|
算法优化:NlogN时间复杂度–可参考(20:41开始)
最大递增子数组和–由LIS O(n2)的办法变化而来的,对应的模板题:
LIS变形:
作者:老陈
1 |
|
1 |
|
1 |
|
注意到达结束条件时,有gcd(a,0) = 1*a+0*b,即(x,y)=(1,0)
定义:含有两个未知量𝑥, 𝑦且形如𝑎𝑥 + 𝑏𝑦 = 𝑐的方程。
我们本次要讨论的问题:
我们将忽略𝑎 = 𝑏 = 0的情形,因为这种情况下不是无穷多解就是无解。(取决于𝑐的取值)
1 |
|
很明显,本题需要使用扩展欧几里得算法来解决
有解的条件?
如何获得𝑋 > 0的最小解?
1 |
|
代码中a * b - a - b
的证明:
https://artofproblemsolving.com/wiki/index.php/Chicken_McNugget_Theorem
1 |
|
ACM必要知识培训
作者:老陈
CCPC是国内的,ICPC是国际的(China & International Collegiate Programming Contest)
Wrong Answer:WA | Time Limit Exceeded:TLE/T | Memory Limit Exceeded:MLE/M |
---|---|---|
Accepted:AC | Compile Error:CE | Runtime Error:RE |
Presentation Error:PE | …… |
OI是中学生信息学竞赛,NOI/NOIP/CTSC是省级竞赛、国级竞赛、国家队选拔赛的简称。不做过多介绍
即All Kill,也就是把本场比赛的题目全部AC。
即0 AC,也就是本场比赛一道题都没有通过。
第几题的意思。例如T3就是第三题。
还有很多……
记住一个点:测评姬1s可以计算$10^8$次!所以你可以通过计算时间复杂度估计出计算次数就可以大致知道会不会超时。
内存比较玄乎。
不过注意:数组开成全局变量!
对于128M的内存限制,能开$3.3 × 10^8$的数组。
例:输入a和b,计算a+b。($0 ≤ 𝑎, 𝑏 ≤ 2 × 10^9$)
1 |
|
很明显,当a和b都为$2 × 10^9$时,结果是超过了int的存储范围的。所以我们需要使用long long
才存储结果(输出)。
1 |
|
RE的最常见原因,其中一个常见原因就是数组开太小了。
所以养成一个习惯:开的数组比题目要求的大一点点。
例:给定一个字符串,将所有小写字母变成大写字母。(字符串长度≤1000)
1 |
|
这份代码会获得RE。(为什么?)
另外,注意不要访问数组外的内容。(例如int a[10]访问a[10])
指大体计算方法正确,但是出现多一个或者少一个的现象,从而引发的答案错误(WA)。
例:给定数组,求某一区间[x,y]内所有元素的平均值。
1 |
|
很明显,这里应该除的是y-x+1
,不是y-x
。
栈溢出常常是由递归调用的嵌套层数过多引发的。
超过$10^4$层的递归就不要写了。
例:计算斐波那契数列第n项的值。($1≤n≤10^6,$ 答案对$10^7 + 7$取模)
1 |
|
看书本后面的优先级,不再赘述。
对于多组输入,应该对上一组使用的数组等变量进行清空。
对于程序中的变量,养成习惯进行初始化。
时间复杂度是衡量一个算法速度的标准。
1 |
|
我们会发现,这个循环的计算次数与n的大小有关(n是多少,num++就进行多少次)。所以这个循环的复杂度为O(n)
。
1 |
|
这个句子只是个简单的输出语句,无论n多大,这个语句都只执行一次。所以这个循环的复杂度为O(1)
。
1 |
|
这段代码我们发现,当i为某一个值时,第二层循环就会执行n次。而i的范围为0-n-1
。即外层循环执行n次,内层循环执行m次。那么我们可以算出这个循环的计算次数为$𝑛 ^2$次,所以这段代码的时间复杂度为$O(𝑛^2)$。
1 |
|
这个循环的计算次数是多少呢?我们尝试列个表:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
n | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 |
你会发现:若循环至少执行x次,那么n至少为$2^𝑥$。
那么我们可以得到:这段代码的时间复杂度为$O(log2𝑛)$
特殊的,对于2为底的对数,我们可以省略不写,即$O(log𝑛)$。
很多函数的时间复杂度也需要记忆,做题时最好计算次数以保证不会超时后再提交,避免不必要的罚时。
嵌套的循环总复杂度为各层复杂度相乘。不嵌套的语句复杂度为相加,忽略系数,仅保留最大项。
例如有段代码是这样的:
1 |
|
我们可以看到,一开始有个两层的嵌套循环。第一层复杂度为$O(𝑛) $(注意忽略系数),第二层为$O(n)$。那么这个循环的总复杂度为$O(𝑛^2)$。
接下来一个$O(n)$的循环内调用了sort函数,sort函数的复杂度$O(𝑛logn)$,所以这段代码的复杂度为$O(𝑛^2log𝑛)$。最后两行输出语句复杂度均为O(1)。那么总复杂度相加,为$O(𝑛^2log𝑛+2)$,保留最大项,忽略系数,我们得到了最终的复杂度:$O(𝑛^2log𝑛)$。
我们看一下下面的代码,计算一下时间复杂度:
最差/最优情况:代码在最糟糕/最理想的情况下的时间复杂度。
以数组查询为例:
1 |
|
这段代码最差情况为x不存在与数组num中,此时这段代码的时间复杂度为O(n)
。
最优情况是第一个数就是x,此时这段代码的时间复杂度为O(1)
。
平均复杂度:代码在所有情况下的平均时间复杂度。
均摊复杂度:代码在大部分情况下出现的时间复杂度。
例如:我现在有个长度为n的数组,如果数组没满,我就向数组内插入一个数;如果满了,我就计算数组的所有元素的和。
我们可以得到平均复杂度为$O(\frac{n-1+n}{n})=O(n)$,而均摊复杂度是$O(1)$。
]]>作者:肖锐
本次比赛设置赛题8道,其难度不一,时间长度为2.5小时,按照OI赛制选拔队员。
赛后部分题目讲解
题目 | 难度 |
---|---|
A 老陈的口算题 | 简单 |
B 老陈的校验码 | 较简单 |
C 老陈的字符串 | 较难 |
D 老陈的七彩石 | 难 |
E 老陈的三角形 | 较难 |
F 老陈的海岸线 | 难 |
G 老陈的日记 | 较简单 |
H 老陈的集合 | 简单 |
直接循环输入a、b、c后判断a+b是否等于c即可。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#include <stdio.h>
int main()
{
int n;
scanf("%d", &n);
int ans1 = 0,ans2 = 0;
for(int i = 0;i < n;i ++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
if(a + b == c)
{
ans1 ++;
}
else
{
ans2 ++;
}
}
printf("%d %d",ans1,ans2);
return 0;
}
/**************************************************************
Problem: 4481
Language: C++
Result: 正确
Time:3 ms
Memory:1080 kb
****************************************************************/
对于一级校验码,遍历一遍输入的字符串,统计0-9,A-F的数量。(注意F之后的字母不要算)
统计之后将每个数转化成十六进制后形成所要的校验码。
对于K>1的情形,重复上面操作即可。
我们可以遍历存下A、C、M这三个字母在字符串中出现的位置。
对于每一个A的位置,在题目要求的长度范围内找到第一个C。(利用所得的所有C的位置)
之后计算这个C到要求长度范围内有几个M。
累加即可。
注意答案超过int范围。
对于现在有的石头和需要的石头,我们可以相减一下得到还需要多少个或者多了多少个这种石头。
遇到需要补充的石头直接从离它最近的高等级石头开始砸起,计数即可。
(对于次数可以先预算一下。例如一块紫色石头能砸出多少个红色石头,要砸多少次之类,加快速度)
计算三角形三边的长度。(两点间距离)
如果三边都是非整数那么肯定不能让三角形变成符合要求的三角形。
如果三边都是整数且不一样,那么就已经符合要求了。
如果有两边一样或者有两边不是整数,那么我们需要判断是整数的那条边是否是1。如果是,那么就无法得到符合要求的三角形;否则可以。(利用三角形两边之和大于第三边可以得到不存在)
如果有两边是整数,判断两边之中大者是不是1即可。(同上判断)
利用bfs/dfs算法可以解决,重点在边长的计算。(不详述)
本题两个问题:判断日期是否合法,判断日期大小
是否合法(yy/mm/dd)
:
注意判断dd是否在对应月份的天数内。mm不能超过12。注意判断闰年,注意负数。
判断日期大小:
年份小的必定小,年份一样看月份,月份一样看日。
注意判断日期合法后才能判断大小。
1 |
|
可以使用一个数组将输入的所有数放在一个数组里后排序,每输出一个数检查是否是上一个输出的数就行了。
坑点:题目写着“均不超过1000”,但可能是一个很大的负数,所以这题要使用long long来存数据才能通过,否则最多92分。
如果使用C++,set会很便利。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44#include <stdio.h>
#include <set>
/*
*set的含义是集合,它是一个有序的容器,里面的元素都是排序好的,支持插入,删除,查找等操作,就像一个集合一样。
*所有的操作的都是严格在logn时间之内完成,效率非常高。
*set容器中的值都是唯一的,即不能再插入一个容器中已经有的值。
*/
using namespace std;
int main()
{
set<long long> s; //创建一个空的set容器,里面元素的类型为long long
int n, m;
scanf("%d%d", &n, &m);
long long t;
for(int i = 0;i < n;i ++)
{
scanf("%lld", &t);
s.insert(t); //把t插入到容器中,容器中没有这个值的话成功插入,否则插入失败。
}
for(int i = 0;i < m;i ++)
{
scanf("%lld", &t);
s.insert(t);
}
set<long long>::iterator itr;
//遍历set,因为set是有序,唯一的,所以直接输出就是答案
for(itr = s.begin();itr != s.end();itr ++)
{
printf("%lld ",*itr);
}
return 0;
}
/**************************************************************
Problem: 4488
Language: C++
Result: 正确
Time:3 ms
Memory:1204 kb
****************************************************************/
本次比赛举办得较好,有力得促进了各参赛者的编程水平以及提高其编程积极性。
感谢出题人以及队内管理人员的付出。
2018即将过去,迎来2019,在新一年里,集训队要争取更好成绩,push
作者:肖锐、吴兆恒
平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴。对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程算出两个矩形的交的面积。
输入格式
输入仅包含两行,每行描述一个矩形。
在每行中,给出矩形的一对相对顶点的坐标,每个点的坐标都用两个绝对值不超过10^7的实数表示。
输出格式
输出仅包含一个实数,为交的面积,保留到小数后两位。
样例输入
1 1 3 3
2 2 4 4
样例输出
1.00
`
题目可提交于HDU2056 Rectangles
可以看出,两个矩形交叉的部分(如果有)也是一个矩形,只需要知道交叉部分矩形的长和宽,就可以求出交叉部分的面积了。给出的8个数字是两个矩形左下角、右上角共计4个点的坐标,可以用它们求出交叉部分矩形的长和宽。
接着则是求出y和x,那么如何求出y和x呢
现在以求y为例子,我们通过上面的图可以发现,(y2-y1)+(y3-y4)
正好等于(y3-y1+y)
,于是我们就有
y=(y2-y1)+(y3-y4)-(y3-y1)
,因为给出的坐标不确定哪个大哪个小,所以得加上绝对值,于是就有
y=fabs(y2-y1)+fabs(y3-y4)-(y3-y1)
。我们可以发现y3
为纵坐标中的最大值而y1
为纵坐标中的最小值。
通过上面的图,我们发现fabs(y2-y1)+fabs(y3-y4)
代表的意义没有变,但是后面不一定是减去y3-y1
,而是减去纵坐标中的最大值和纵坐标中的最小值,故有y=fabs(y2-y1)+fabs(y3-y4)-(ymax-ymin)
。
x同理。
1 |
|
手动判断各个矩阵的关系
详情见HDU 2056 Rectangles(计算相交面积)
暴力枚举各种情况
详情见LeetCode:Rectangle Area - 矩形交叉部分的面积里面的解法2
为什么1小时有60分钟,而不是100分钟呢?这是历史上的习惯导致。
但也并非纯粹的偶然:60是个优秀的数字,它的因子比较多。事实上,它是1至6的每个数字的倍数。即1,2,3,4,5,6
都是可以除尽60。
我们希望寻找到能除尽1至n的的每个数字的最小整数。
不要小看这个数字,它可能十分大,比如n=100, 则该数为:69720375229712477164533808935312303556800
请编写程序,实现对用户输入的 n (n<100)求出1~n的最小公倍数。
输入格式
输入包含若干行整数:第1行为一个整数m,表示样例的组数;接下来有m行,每行为一个整数n,范围为[1,100],表示求1~n的最小公倍数。
输出格式
输出多行,每行为每个测试用例的最小的公倍数。
样例输入
2
6
10
样例输出
60
2520
两个数的最小公倍数就是不断找到该两个数的公约数并两个数同时除去该两个数,最后该两个数互质;
那么,1到n的最小公倍数,是1到n-1的最小公倍数乘以n的所有素因子中没有被1到n-1包含的素因子。
例如,1到7的最小公倍数是2*3*2*5*7
,8=2*2*2
,(8中2出现3次,1到7的素因子中只出现2次)那么1到8就是2*3*2*5*7*2
所以,需要将[1, n]
的最小公倍数求出,比如6前面如果有2 3就可以用1替代。运用双重循环,如果这个数字可以被前面任意一个数整除,则被替换,后来只需要将这些数字乘起来即可,因为结果会很大,所以我们得用到大数乘法。
现在可以假设模拟一下43*65
,假设num数组存储43(因为数字变大顺序是从右到左,故num[0]存储的3),并且乘以13后更新数组。
基本流程是让num每一位去乘以n,然后对结果模10,即取出最后一位,因为有进位,所以将结果/10
后就是对于的进位了。
c是代表进位的值
当处理完num时,因为还有进位没有处理完,故还得继续处理进位
1 |
|
题目可提交于XYNUOJ1488
N个人要打水,有M个水龙头,第i个人打水所需时间为Ti,请安排一个合理的方案使得所有人的等待时间之和尽量小。
第一行两个正整数N M 接下来一行N个正整数Ti。
N,M<=1000,Ti<=1000
最小的等待时间之和。(不需要输出具体的安排方案)
7 3
3 6 1 4 2 5 7
11
一种最佳打水方案是,将N个人按照Ti从小到大的顺序依次分配到M个龙头打水。
例如样例中,Ti从小到大排序为1,2,3,4,5,6,7,将他们依次分配到3个龙头,则去龙头一打水的为1,4,7;去龙头二打水的为2,5;去第三个龙头打水的为3,6。
第一个龙头打水的人总等待时间 = 0 + 1 + (1 + 4) = 6
第二个龙头打水的人总等待时间 = 0 + 2 = 2
第三个龙头打水的人总等待时间 = 0 + 3 = 3
所以总的等待时间 = 6 + 2 + 3 = 11
这题只需要把所有人的打水时间排序,然后每个人依次分到每个水龙头. 接着计算时间即可 用一个数组存每个人的打数时间+等待时间. 把所有人的时间加起来即是总时间
下面是无聊写的一大段分析 有兴趣就看 有可能会有说错的地方 有问题请提
根据题目给出第提示可以发现一个规律. 把所有人的打水时间进行一次排序后. 把排序后的每个人按顺序分配给每个水龙头. (这里顺序分配的意思就是 例如有5个人a,b,c,d,e 2个水龙头A,B 并假设5人排序后顺序还是a,b,c,d,e. 那么顺序分配的意思就是 第一个人到A 第二个人到B 接着第三个人又回到A 一个循环直到所有人分配好)
测试发现这样得出的答案是最小等待时间.
不考虑提示. 如果想要M个水龙头 N个人打水等待时间最小. 首先想一下最大时间是怎样算的.
假设 4个人a,b,c,d 打水时间分别为10000,2,3,9999 有1个水龙头
想要算最大时间. 首先已经知道要把所有人放在一个水龙头上. 那么接下来还要怎么做呢. 很明显是把打水时间越长的放越前面. 这样后面的人就要等的越久了.
a d c b 这样的打水顺序
d c b都要等a 于是这里的时间为 T += 10000 * 3
c b 又要等d T += 9999 * 2
b 等c T+= 3
T = 50001
得出想要算最大时间 需要把打水时间越久的人放在越前. 算最小时间于是就是反过来. 因此需要用到的排序
重新整理下 算最小时间 可知顺序为 b c d a
c d a等b T += 2 * 3
d a 等c T += 3 * 2
a等d T += 9999
T = 10011
接下来考虑多个水龙头的情况
如果有多个水龙头的话. 根据前面只有一个水龙头的例子应该可以想出. 打水时间长的人 应该尽量分散到不同到水龙头.
假设还是上面的假设 只是换成两个水龙头
如果要求最小 毫无疑问你最想做的应该就是把9999和10000分开 以成下面这样(竖着看)
1 |
|
这样的时间T会是
水龙头A = 2*2 + 3 = 7
水龙头B = 0
T = 7
但是好像还不是最优 因为水龙头A有3人 b后面有两人 因此b的时间要算上两倍 而水龙头B上只有1人.
尝试让每个水龙头的人数均匀起来 把c分配到水龙头B
1 |
|
此时的时间T为
水龙头A = 2
水龙头B = 3
T = 5
1 |
|
题目可提交于NYOJ519
在对银行账户等重要权限设置密码的时候,我们常常遇到这样的烦恼:如果为了好记用生日吧,容易被破解,不安全;如果设置不好记的密码,又担心自己也会忘记;如果写在纸上,担心纸张被别人发现或弄丢了…
这个程序的任务就是把一串拼音字母转换为6位数字(密码)。
我们可以使用任何好记的拼音串(比如名字,王喜明,就写:wangximing)作为输入,程序输出6位数字。
变换的过程如下:
第一步. 把字符串6个一组折叠起来,比如wangximing则变为:
wangxi
ming
第二步. 把所有垂直在同一个位置的字符的ascii码值相加,得出6个数字,如上面的例子,则得出:
228 202 220 206 120 105
第三步. 再把每个数字“缩位”处理:就是把每个位的数字相加,得出的数字如果不是一位数字,就再缩位,直到变成一位数字为止。例如: 228 => 2+2+8=12 => 1+2=3
上面的数字缩位后变为:344836, 这就是程序最终的输出结果!
要求程序从键盘输入数据,输入格式为:第一行是一个整数n(0<n<100),表示下边有多少输入行,接下来是n行字符串,就是等待变换的字符串。
输出格式为:n行变换后的6位密码。
3
zhangfeng
wangximing
jiujingfazi
772243
344836
297332
对于这条题目 题意应该很容易明白. 对于大家来说可能难在代码的实现. 其实实现也是很简单的. 下面一步步讲题目的操作实现
1 |
|
学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
输入格式
包含若干测试用例,每个测试用例:
第一行两个整数n, m,为迷宫的长宽。
接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。
输出格式
第一行一个数为需要的最少步数K。
第二行K个字符,每个字符∈{U,D,L,R}
,分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。
样例输入
3 3
001
100
110
3 3
000
000
000
样例输出
4
RDRD
4
DDRR
数据规模
有20%
的数据满足:1<=n,m<=10
有50%
的数据满足:1<=n,m<=50
有100%
的数据满足:1<=n,m<=500
参考:
http://www.cnblogs.com/chiweiming/p/9406530.html
此题用BFS和DFS都能得出结果,但是用DFS会超时,用BFS一旦找到迷宫出口,即可确定最短径,
而DFS搜索出每一条可达路径,然后需要比较步数才能确定最短路径。
此题需要解决的两个问题:
1 |
|
作者:老陈
时间限制:1s 内存限制:256MB
题目描述
lahub和lahubina去一家豪华餐厅约会。一切都很好直到结账。服务员要的不是钱,而是让lahub写一个带有n个数的饥饿序列。
一个包含n个正整数的序列$a_1,a_2,…,a_n$是个饥饿序列当且仅当:
序列单调递增
对于序列中任意两个数,均不能整除。
lahub遇到了麻烦,所以他向你求助。帮帮他!
输入描述
仅包含一个正整数n。($1≤n≤10^5$)
输出描述
输出一行包含n个数代表一个可能的饥饿序列$a1,a2,…,an(1≤ ai ≤ 10^7 )$
如果有多个符合要求的序列,请输出任意一个
Sample Input | Sample Output |
---|---|
3 | 2 9 15 |
5 | 11 14 20 27 31 |
分析
对于题目描述,我们可以发现一个很关键的地方:
对于序列中任意两个数,均不能整除即一个数不可能成为另外一个数的因子。
但注意并不是一定互质。例如:9 12
但是,很明显,质数序列肯定符合这个要求。
所以仅需输出质数序列的前n项即可。
获得质数序列可以使用素数筛,不再赘述。
时间限制:3s 内存限制:256MB
题目描述
两个士兵在玩游戏。一开始,他们中的一个选择了一个正整数 n 并把它给第二个士兵。然后第二个士兵会在每一回合尝试找到最大的数。每一轮包括选择一个正整数 x > 1,使 n 能被 x 整除,并将 n 替换为 n / x 。当 n 等于 1,并且没有更多可能有效的移动时,游戏结束,第二名士兵的得分等于他执行的回合数。
为了使游戏更有趣,第一名士兵选择的 n 是 a!/b!(这里的a和b是正整数且 $a ≥ b$)。
第二名士兵的最高分是多少?
输入描述
第一行包含一个正整数t ,代表第二个士兵玩的游戏场数。($1≤ t ≤10^6$)
接下来 t 行,每行输入两个正整数a 和 b($1 ≤ b ≤ a ≤5×10^6$),代表游戏中的整数n。
输出描述
对于每场游戏输出第二个士兵的最大得分。
Sample Input
2
3 1
6 3
Sample Output
2
5
分析
我们容易知道,这是一道求质因子个数的题目。
对于一个数 n ,我们的 x 一直取 n 的质因子就可以得到最高分。
但是,
$$
𝑛=\frac{a!}{𝑏!}=(𝑏+1)(𝑏+2)…(𝑎−1)𝑎
$$
那么,对于一个问题,答案就是从 b+1 到 a 的所有数的质因子个数之和。
那么,对于一个问题,答案就是从 b+1 到 a 的所有数的质因子个数之和。
那么,怎么求一个数的质因子个数呢?
首先,质数因子嘛,要求一下质数表。
然后对于这个数,从2开始一个一个试除,能整除就质因子个数+1。
考虑一下最差情况,
$$
t= 10^6, b=1, a= 5×10^6
$$
我们需要对 $ 5×10^6 $个数求质因子个数 ,假设求因子个数的时间复杂度是O(1)(并不是)
个数求质因子个数 ,假设求因子个数的时间复杂度是O(1)(并不是)
,我们需要进行 $5×10^{12}$次运算。明显超时。
有没有更好的做法?
我们可以模仿素数筛的做法来得到 [1, $5×10^6$]的所有数的因子数。
对于每个询问区间,我们可以用前缀和处理一下。
对于一次询问,我们只需O(1)即可解决。
对于最差情况,我们仅需大约$1.1×10^8$次运算即可,符合时间要求。
1 |
|
时间限制:5s 内存限制:128MB
题目描述
Bob每戒一次烟,就会更换电子邮件的密码。密码总以no-smokX
的形式设置。X则是个位数以上的自然数。不仅如此,第k次戒烟时,Bob会选择具有k个约数的数值X。例如,第12次戒烟时修改的密码是no-smok486
。486有1、2、3、6、9、…、243、486
共12个约数。
有一天,Bob睡觉前下定决心要戒烟,并修改了邮箱密码。第二天清晨,他发现自己忘记了新设定的密码,只记得密码中的数值具有n个约数,且取值范围是[l,r]
(包含l和r)。
试编写程序,计算取值范围内共有几个可能的密码。
输入描述
第一行包含一个正整数t ,代表测试用例个数。($1≤ t ≤ {50}$)
接下来 t 行,每行输入三个正整数n、l 和 r($n < 400,1 ≤ l ≤ r ≤1×10^7,r-l < 1×10^6 $)。
输出描述
对于每个测试用例,输出答案。
Sample Input
3
2 2 10
12 486 486
200 1000000 2000000
Sample Output
4
1
19
本题要求的是[l,r]
中有多少个数的约数个数是n。
本题的重点在于如何快速求约数个数。
约数个数公式:
我们发现这里用到了质因数分解。所以我们可以使用上一题的质因数分解来做。
总时间复杂度O(t(r-l)+nloglogn)
,最差情况下运算次数大约为$1×10^8$次,符合要求。
更简便的做法:
1 |
|
乍一看,貌似会超时?
这段代码的时间复杂度是O(n logn)
即总时间复杂度O(t(r-l)+nlogn)
,最差情况下运算次数大约为$3.2×10^8$次,符合要求。
时间限制:1s 内存限制:64MB
题目描述
给定一个整数N,求最小的 x (x > 0)使得$2^x mod n=1$。
输入描述
输入包含多组测试用例,每个测试用例包含一个正整数N。
输出描述
若存在最小的 x,请输出“2^xmod n = 1”
;否则输出“2^?mod n = 1”
。
Sample Input | Sample Output |
---|---|
2 | 2^? mod n = 1 |
5 | 2^4 mod 5 = 1 |
此题暴力可A,不再赘述。(可能是数据水)
但是数据一大,暴力就不行了。
欧拉函数:$φ(n)——[1,n]$内与n互质的数的个数。
当n为质数时,$φ(n)=n-1$。
当$n=p^k$时(p是素数),$φ(n)=φ(p^k )=p^k-p^{k-1}=(p-1)p^{k-1}$
若n,m互质,$φ(nm)=φ(n)φ(m)=(n-1)(m-1)$
若n是奇数,则$φ(2n)=φ(n)$
由上面的性质,我们可以写出求欧拉函数的一般式:
我们设正整数n的唯一分解为$p_1^{a_1} p_2^{a_2 }…p_i^{a_i }$。其中,$ p_1,p_2,…,p_i$为素数。
则我们可以得到:
两个重要定理:
延伸:
小于n且与n互质的数的和:
$$
\frac{φ(n)∗n}2 (n>1)
$$
应用:
求$7^{222}$的个位数。
因为7和10互质,且$φ(10)=4$
所以$7^4 mod 10=1$
所以$7^{222} mod 10=7^{4∗55}∗7^2 mod 10=7^2 mod 10=9$
即$7^{222} mod 10=7^{222\%4} mod 10=7^2 mod 10=9$
因为涉及唯一分解,那就与前面一样进行分解,边分解边计算即可。
1 |
|
解题——2^x mod n = 1
回到正题。
首先,我们能确定n=1时肯定无解。
对于n为偶数,我们知道$2^x$肯定是个偶数,那么模一个偶数的结果不可能是1。
所以当n为偶数时同样无解。
对于n为奇数,我们根据欧拉定理可以知道$φ(n)$是符合题目要求的一个数。所以n为奇数时一定有解。
但是,题目要求x最小,但是$φ(n)$不一定是最小值。
我们假设r是问题的解,即r是最小的x使得$2^x \mod n=1$。
那么对于其他的数X,若有$2^X \mod n=1$。
那我们可以得到$X \mod r=0$。
所以,我们可以遍历$φ(n)$的所有因子就可以得到答案。
模运算的运算律:
加法:
$(a+b)\mod n=(a \mod n+b \mod n)\mod n$
减法:
$(a-b)\mod n=(a \mod n-b \mod n)\mod n$
乘法:
$(a∗b)\mod n=(a \mod n∗b \mod n)\mod n$
幂:
$a^b \mod n=(a \mod n)^b mod n$
注意!除法不成立。
那么,如何计算$(a/b)\mod n$?
我们可以通过将除b转换成乘b的逆元来解决,即$(a∗b^{-1} )\mod n$ 。
逆元的定义:
$bc \mod p≡1$
此时c称为b的逆元。
容易知道,只有b和p互质的时候,c才有解;否则无解。
那么c怎么求?
联想费马小定理:
$a^{n-1} ≡1(mod n)$
我们可以得到:
$b∗b^{n-2} ≡1(mod n)$
即$c=b^{n-2} $ 。
Codeforces | 洛谷 |
---|---|
755A | P1036 |
948B | P1075 |
1047C | P1125 |
432C | P1832 |
236B | P2398 |
385C | P3383 |
151C | P2424 |
327C | P1306 |
615D | P1405 |
P3811 |
作者:老陈
Sample Input
5
2 3 5 9 7
5
1 2
2 3
4 5
1 5
3 5
Sample Output
2.500
4.000
8.000
5.200
7.000
分析
我们考虑一下这种解法的时间复杂度:首先,我们需要构造psum[]
数组,这需要O(N)的时间。然后对于每一个查询,我们可以得知需要O(1)的时间。所以总时间复杂度为O(N),这符合题目时限要求。
1 |
|
内存能否再优化???是否注意到这里的grade[]
数组在后面的询问中根本就没起到作用?此处可以不使用grade[]
数组。
1 |
|
Sample Input
5
2 3 5 9 7
5
1 2
2 3
4 5
1 5
3 5
Sample Output
0.25000
1.00000
1.00000
6.56000
2.66667
1 |
|
Sample Input
5
1 2 3 5 6
9 8 7 6 5
1 2 7 9 5
2 5 0 8 7
3 6 9 4 5
3
1 2 3 4
3 4 1 3
1 5 1 5
Sample Output
10
37
125
1 |
|
时间限制:2s 内存限制:256MB
题目描述
给定一个由n个小写字母组成的字符串s。Polycarp想从s中删除k个字母。 ( k≤n)Polycarp使用下面的算法k次:
如果存在至少一个’a’,则删除最左的一个结束算法
如果存在至少一个’b’,则删除最左的一个结束算法
…
如果存在至少一个’z’,则删除最左的一个结束算法
这个算法删除了字符串s中的一个字母,Polycarp执行这个算法正好k次,也就删除了k个字母。帮助Polycarp得到结果字符串。
Sample Input
15 3
cccaabababaccbc
1
Sample Output
cccbbabaccbc
Sample Input
4 15 9
cccaabababaccbc
2
5 1 2 1
Sample Output
cccccc
Sample Input
1 1
u
Sample Output
1 |
|
作者:老陈
题目描述
老陈最近遇到了一个问题,他想计算一个四边形的面积。但是由于他遇到的四边形都是奇形怪状的所以他不想动手算了。你能否写一个程序帮他完成计算?
输入
输入共四行,每行代表四边形的其中一个顶点的坐标。
输出
输出一个数代表这四个点围成的四边形的面积,保留三位小数。
样例输入
2 2
2 -2
-2 2
-2 -2
样例输出
16.000
思路
利用割补法进行计算。
参考代码
略
题目描述
老陈某天夜里,做了一场噩梦。他梦见他受到了无数个无形的力的作用。而他的前方Y米处是一处悬崖。此时他会受到这些力作用M秒,在这段时间内老陈无法做出任何动作,这些力也不会发生改变。
他想知道,M秒过后,他会不会掉下悬崖。
输入
第一行包含三个正整数N、M、Y,分别表示老陈受到的力的个数、受到这些力的时间、老陈离悬崖的距离。($1≤N≤10^5, 0≤M≤10^5,1≤Y≤10^9$)
接下来N行,每行输入一个力的信息。这些力的表示均使用向量表示。老陈位于原点,悬崖在老陈的y轴方向。
输出
若老陈在M秒后会掉下悬崖,输出”Die”;否则输出”Save”。
样例输入
3 8 5
1 1 0
-1 0 0
0 -1 0
样例输出
Save
思路
参考代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18int main()
{
int n, m, total_y = 0;
ll k;
cin >> n >> m >> k;
for (int i = 0; i < n; i++)
{
int x, y, z;
cin >> x >> y >> z;
total_y += y;
}
if (k - (ll)m*total_y <= 0)
cout << "Die";
else
cout << "Save";
return 0;
}
题目描述
我们知道,计算$2^n\mod m$是一个著名的问题。快速解决这个问题的算法是RSA加密算法的核心之一。
现在,我们想解决与问题“相反”的另一个问题:计算$m \mod2^n$
请你完成这个问题。
输入描述
仅包含两个正整数n、m。($1≤n,m≤10^8$)
输出描述
输出一个整数,代表$m \mod2^n$的值。
样例输入
4 42
样例输出
10
思路
注意数据范围!$n≤10^8$
你能计算$2^{100000000}$吗?
当然不可能!
那么怎么办呢?
回到数据范围,m的范围也是最大为$10^8$
计算器算一下,我们知道m不会大于$2^{27}$。那么,如果n大于27的话,很明显,最终式子的最终结果是m。如果n不大于27,我们就老老实实模一下就行了。(反正不会超过int范围)
参考代码1
2
3
4
5
6
7
8
9int main()
{
int n, m;
scanf("%d%d", &n, &m);
if (n > 27)
printf("%d", m);
else
printf("%d", m % (int)pow((double)2, (double)n)); return 0;
}
题目描述
玩家A和玩家B现在正在魔塔中闯关。这座魔塔共有X层,要想打败这一层的领主才能前往下一关。
玩家A和玩家B很菜,他们自身的攻击力连第一层的领主都打不过。所以他们想到了另外一种方法:利用领主害怕的符咒来杀死领主。玩家A和玩家B的背包中都有一定数量的符咒并且玩家A拥有一个可以使用Y次的符咒转换器,它能够将任意一张符咒转换为另一张符咒。
现在他们准备出发了,请你判断下他们能否成功通关。
输入描述
第一行包含两个正整数X、Y,分别代表魔塔的层数和符咒转换器的使用次数。($1≤x,y≤10^5$)
第二行代表玩家A中拥有的符咒的情况,其中第一个数代表玩家A拥有的符咒的数量。其后有个正整数,代表第i个符咒能作用于第i层的领主上。
第三行代表玩家B中拥有的符咒的情况,形式与第二行相同。
输出描述
如果他们能够通关,输出“Yes”,然后空一个空格后输出使用符咒转换器的次数;如果他们无法通关,输出“No”。
样例输入
5 3
4 1 2 2 2
2 4 5
样例输出
Yes 1
思路
我们考虑一下使用符咒转换器的条件:
如果转换之后能补齐,那么就是Yes,而使用的次数就是之前缺失的符咒的张数。
Set简介
Set是一个重要的容器。Set,顾名思义就是集合。
学过数学,我们知道,集合满足不重复性。所以Set的一个特点是不允许有重复元素。
对于这道题,我们可以使用Set来存储这些符咒。在保存之前,我们需要判断一下它是否已经存在于Set中。如果存在,就是可以拿来转换的符咒。
在存储完之后,我们使用size函数获得Set中的元素个数。也就获得了有多少种不同的符咒
然后再判断多余的能否转换补齐剩下的即可。
参考代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27int main()
{
set<int>fu;
int n, m;
int k = 0;
cin >> n >> m;
for (int i = 0; i < 2; i++)
{
int t;
cin >> t;
for (int j = 0; j < t; j++)
{
int x;
cin >> x;
if (!fu.insert(x).second)
k++;
}
}
k = min(k, m);
if (fu.size() + k >= n)
{
cout << "Yes " << n - fu.size();
}
else
cout << "No";
return 0;
}
题目描述
小陈正在和他的朋友玩猜数游戏,小陈写了一个数字后告诉朋友这个数的位数和这个数的所有的位的和。
请你计算一下他的朋友可以猜的数的范围。
只包含两个数a、b,分别代表小陈所写的数的位数和这个数的所有的位的和。(1≤a≤100,0≤b≤900)
输出
输出两个正整数,分别代表他的朋友所猜的数的最小值和最大值。
如果不存在符合情况的数,输出“-1 -1”。
样例输入
2 15
样例输出
69 96
思路
这题比较恶心。
细节多。
首先,当总和大于位数*9或者位数大于1但是和为0的时候,就只能输出”-1 -1”了。(注意当位数为1时存在总和为0的情况),然后,确保存在之后,我们确定最大最小值即可。
最大值比较简单,把所有能组成的9放在前面,最后剩下多少就接在9的后面,如果现在位数还不够的话后面补0就行了。
例如:14 92
我们能弄出10个9,最后剩下一个2。然后现在总共11位,还差3位,那就最后补3个0就是了。即99999999992000
。
最小值比较麻烦,我们首先要跟最大值反着来。
把所有的9放在最后面,最后剩下多少就放在最前面。如果位数不够,那么现在最前面的那个数要减一,然后前面补1000……直到位数足够。
还是上面的那个例子:
我们将10个9放在最后,剩下2放在最前面。
即29999999999
。
但是现在只有11位,那么最前面的那一位就要减一。
即 19999999999
。
然后前面用1000……接上直到位数足够。
即 10019999999999
。
按照上面的思路写就完成了。
参考代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41int main()
{
int len, sum;
cin >> len >> sum;
int max[110] = { 0 };
int min[110] = { 0 };
int min_pos = len-1, max_pos = 0;
if ((sum == 0&&len>1)||sum>len*9)
{
cout << "-1 -1";
return 0;
}
int i;
for (i = 1; i * 9 <= sum; i++)
max[max_pos++] = 9;
i--;
if (sum - i * 9)
{
max[max_pos++] = sum - i * 9;
}
for (i = 1; i * 9 <= sum; i++)
min[min_pos--] = 9;
i--;
if (sum - i * 9)
{
min[min_pos] = sum - i * 9;
}
while (min[min_pos] == 0&&min_pos<len-1)
min_pos++;
if (min[min_pos] > 0&&min[0]==0)
{
min[min_pos]--;
min[0]++;
}
for (i = 0; i < len; i++)
cout << min[i];
cout << ' ';
for (i = 0; i < len; i++)
cout << max[i];
return 0;
}
作者:吴兆恒
队列是一种特殊的线性表. 是一种先进先出的数据结构. 它只允许表的前端进行删除操作, 对表的后端进行插入操作.
把队列想象成生活中的排队即可. 例如在商场最后结账的时候, 服务员在前面进行结账操作. 顾客从后面走进去 (相当于从后端插入) 走到进去服务员那里结账完以后离开(相当于从前端删除) 如果有多个顾客在排队, 那么新加入的顾客就排在他们后面 直到轮到他才离开(后进后出)
在c++的STL中有队列相关的头文件, 使用了以后就能直接定义一个队列了. 当然这个头文件不仅仅只有queue 还有
很多 这个以后再说
#include <queue>
除了直接使用头文件, 也可以自己使用数组模拟一个队列出来. 据说性能会比stl更好( 而且毕竟手写能够自己对需求进行改变. 但现在不对手写的进行说明 有兴趣可以去搜相关的资料
那么写了头文件后怎么定义一个队列呢 语句是这样的
1 |
|
type 是类型的意思 可以替换成你想指定的类型 例如int double等 而name就是名字 这个就看个人喜欢
使用stl现成的队列, 队列会带有多种方法. 例如插入删除等 下面介绍每种的使用方法
插入, 正确的说法应该是入列 通过使用队列中的push
方法 通过变量名加圆点(.) 调用方法
1 |
|
出列使用的方法是pop
这个方法不需要任何参数 使用该方法后将自动返回队列中最前面的元素
1 |
|
front方法能让我们直接得到队列中第一个元素
1 |
|
back
方法能让我们获得队列中最后一个元素
1 |
|
size
方法将返回队列中的元素个数
1 |
|
empty
方法是用来告知队列是否为空 如果队列是空的时候 将返回true
反之则false
1 |
|
size
和empty
这两个方法 通常用在循环条件上. 当队列元素数量为0 / 队列是空(两者等价)的时候 跳出循环
bfs是一种图的搜索算法. 是一种盲目搜寻的方法. 换句话说就是暴力. 通过不断的展开直到搜索完整张图
bfs一般都会通过队列实现, 为什么要用到队列. 就关乎到队列的先进先出这个特点了.
拿走迷宫作为例子, 一个人在迷宫中只能上下左右的行走, 如果是障碍则不能通过 如图
圈圈就是起点 点是可以走的道路X
代表路障*
代表目的地
那么从起点开始进行bfs 第一轮进行搜索就是碰到圈圈上下左右的点 (假设顺序是从上右下左)
那么最先碰到的是上方的点 并把上面的点记录 放进队列中
然后继续右,下,左 发现是可以走的路 以此 放进队列中
接下来 因为队列中所存放的都是可以走的路嘛 拿出队列中的第一个元素 (因为是要最先放进的) 搜索这个点的上下左右 会发现 左右是障碍走不了 下是起点 已经走过了不能再走 那就只能走上了. 并把上方的点放进队列中
在这个操作以后, 接着把队列中的元素出列 他们会是之前原点所插入的”右下左” 都进行搜索后会发现图变成这样了
(突然多了红色只是为了方便下文说明 并没有对他什么特别操作)
接下来 队列出列的元素就是红色标记的点了. 搜索后就会发现目的地 到达终点 直接break跳出循环 bfs结束
拿luoguP1683举例 把数据和整个迷宫读入后. 通过遍历找出起点@的位置
容易发现 起点的坐标为(4,6)(下标从0开始计算) 把起点送进队列中
1 |
|
之所以要弄一个结构体node 是因为要保存bfs每一步的所在的位置 (也可以使用pair) 但是结构体都学过 pair以后再讲
接下来就是bfs算法的具体代码 一直循环进行直到整个迷宫探索结束
1 |
|
作者:肖锐
判断左值是哪个,再判断’=’后面是变量还是值
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
BFS小改动
1 |
|
1 |
|
BFS联通块
所有不在圈内的0组成的块,必定会触碰边界。
所以从边界上的0开始进行广搜,把搜过的进行标记,那么没搜过的也不是1的就是要找的2了。
1 |
|