日常刷题记录
雾粉与数论
算法思路
这道题的思路是打表找到
i
i
i 与
gcd
(
i
∗
(
i
−
1
)
2
,
i
∗
(
i
+
1
)
2
)
\gcd(\frac {i*(i-1)} 2, \frac {i*(i+1)} 2)
gcd(2i∗(i−1),2i∗(i+1)) 之间的规律
不难发现当
i
i
i为奇数的时候,结果为
i
i
i,
i
i
i为偶数的时候结果为
i
2
\frac{i}{2}
2i
用等差数列求和公式就能解出答案
代码实现
n=int(input())
ans=((2+n)*(n-1)//2)-(1+n//2)*(n//2)//2
print(ans%(10**9+7))
按纯度筛选
算法思路
这题就是一个排序的问题,但是对于一个二维数组,其中每个元素还包含了多个参数,用python的sort
函数该怎么写才能做到有优先级的根据这些不同的参数给每个元素排序?其实同样用一个匿名函数,但是x
后面得用括号把这些元素按优先级先后顺序括起来,默认升序,如果是按照这个值降序那么要在前面加个-
号。
代码实现
while True:
n=int(input())
if n==0:
break
ans=[]
for i in range(1,n+1):
s=list(input().strip())
ans.append([s,len(set(s)),len(s)])
ans.sort(key=lambda x:(x[1],-x[2],x[0]))#根据多个优先级不同的参数来排序
for i in ans:
print("".join(i[0]))
print()
加密数
算法思路
这个也是一个简单题,主要是想记录一下zfill()
函数的使用,join()
方法和三元操作符
以及生成器表达式
的结合,还有内置的进制转换函数和补码的计算。
str.zfill(n)
可以很方便的实现这题的不足32位在前补0,只要参数填上补0后想得到的长度n
即可,是对字符串使用的。bin(num)[2:]
bin()
函数用于把一个整数转成二进制,得到的结果是字符串型的,而且他默认在得到的二进制数字符串开头加上’0b’所以要切个片- int(str,n) 用于把一个
n
进制的字符串str
转换成十进制 - 补码 十进制负数的二进制通常用补码表示但
bin()
函数不能计算出补码,所以要手动完成。具体操作是按照已知位数(通常是8位,16位,32位),在前面补上0,然后将0变成1,1变成0。以上的操作得到了反码,接着把反码转回十进制,然后加1,再转回二进制,再补0。
代码实现
while True:
try:
n = int(input().strip())
b = bin(abs(n))[2:].zfill(32)
if n < 0:
b = ''.join('1' if i == '0' else '0' for i in b)#反码
b = bin(int(b, 2) + 1)[2:].zfill(32)#补码
ans = sum(int(b[i:i+8], 2) for i in range(0, 32, 8))
print(ans)
except EOFError:
break
求细胞的数量
算法思路
遍历网格,直到找到一个不为0的位置,将其以及与其连通的不为0的位置变成0,计数器加一
代码实现
from collections import deque
def bfs(start_x, start_y, grid, n, m):#五个参数表示起始坐标,网格,行数,列数
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]#四个方向
queue = deque([(start_x, start_y)])#初始化队列为起始坐标
grid[start_x][start_y] = '0' #将起始坐标标记为0
while queue:
x, y = queue.popleft()#弹出队列头部元素
for dx, dy in directions:
new_x, new_y = x + dx, y + dy#计算四个方向的坐标
if 0 <= new_x < n and 0 <= new_y < m and grid[new_x][new_y] != '0':#判断是否在网格内且未被标记
queue.append((new_x, new_y))#将坐标加入队列
grid[new_x][new_y] = '0'#将坐标标记为0,表示访问过 ,在这里不会影响入队后的周围细胞的判断,因为完成一次bfs说明找到了一个细胞
n,m = map(int, input().split())
grid = [list(input()) for _ in range(n)]
num = 0
for i in range(n):
for j in range(m):
if grid[i][j] != '0':#找到一个不为0的位置作为搜索的起点
bfs(i, j, grid, n, m)#通过搜索将与这个位置联通的格子标记为0
num += 1#找到了一个细胞,数量加1
print(num)
围成面积
编程计算由 " 1 " 围成的下列图形的面积。面积的计算方法是统计" 1 “所围成的闭合曲线中水平线和垂直线交点的数目。如下图所示,在10*10的二维数组中,有” 1 "围住了15个点,因此面积为15。如图所示
输入:共有10行10列,每行都有0和1构成,数字之间用空格分隔。
输出:统计10*10正方形中,1围成的图形面积(0的个数)。
算法思路
给整个矩阵外面套一圈0,然后从左上角开始搜索,把与其连通的格子全部变成1,最终统计矩阵中还剩多少0,就是围成的面积。
代码实现
python
from collections import deque
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
def bfs(sx,sy):
que = deque([(sx, sy)])
a[sx][sy] = 1
while que:
i,j = que.popleft()
for dx, dy in directions:
x, y = i + dx, j + dy
if 0 <= x < n+2 and 0 <= y < n+2 and a[x][y] == 0:
a[x][y] = 1
que.append((x, y))
n = 10
a = [[0] * (n + 2) for _ in range(n + 2)]
for i in range(1, n + 1):
a[i][1:n + 1] = map(int, input().split())
bfs(0, 0)
ans = sum(row.count(0) for row in a)
print(ans)
c++
#include<bits/stdc++.h>
using namespace std;
#define N 15
struct Node
{
int x, y;
Node(){}
Node(int a, int b):x(a), y(b){}
};
int n, a[N][N], ans;//a[i][j]:(i,j)位置标记的数字
int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
void bfs(int sx, int sy)
{
queue<Node> que;
a[sx][sy] = 2;
que.push(Node(sx, sy));
while(que.empty() == false)
{
Node u = que.front();
que.pop();
for(int i = 0; i < 4; ++i)
{
int x = u.x + dir[i][0], y = u.y + dir[i][1];
if(x >= 0 && x <= n+1 && y >= 0 && y <= n+1 && a[x][y] == 0)//地图范围为0~n+1
{
a[x][y] = 2;
que.push(Node(x, y));
}
}
}
}
int main()
{
n = 10;//扩展边界后,地图范围:行列 0~n+1
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
cin >> a[i][j];
bfs(0, 0);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
{
if(a[i][j] == 0)
ans++;//面积加1
}
cout << ans;
return 0;
}
营救
算法思路
这道题是求最短路径,而且已知起点和终点,直接广搜。
用一个双端队列,其中每一个元素包含三个值:x坐标,y坐标,路径长度。从起点开始每次访问其上下左右四个位置,如果是可以到达的位置,那么就把这个位置设为’1’避免重复访问。一旦访问的点到达了终点,则当前记录下的路径长度再加1就是所移动的最短距离,直接return。
代码实现
from collections import deque
dir=[(1,0),(-1,0),(0,1),(0,-1)]
def bfs(n, a, sx, sy, ex, ey):
queue = deque([(sx, sy, 0)])
a[sx][sy] = '1'
while queue:
x, y, dist = queue.popleft()
for dx,dy in dir:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < n and a[nx][ny] == '0':
if (nx, ny) == (ex, ey):
return dist + 1
queue.append((nx, ny, dist + 1))
a[nx][ny] = '1'
return -1
n = int(input().strip())
a = [list(input().strip()) for _ in range(n)]
sx, sy, ex, ey = map(int, input().split())
result = bfs(n, a, sx-1, sy-1, ex-1, ey-1)
print(result)
最少拐弯次数
算法思路
最少拐弯次数就是最短路径的情况下的拐弯次数,直接拿上一题营救的代码,然后把原本记录距离的,改成记录拐弯次数,然后新开一个变量D用来记录上一次的方向,观察可知,方向数组中以1,-1开头的是同一个方向,以0开头的是同一个方向,所以取方向数组中每个元组的首元素的绝对值记录在变量D中就可以记录方向了。要注意的是这个变量D一开始不能设为0和1,因为不能算上出发时的转弯,所以设置成2就可以了,最后输出的时候减掉1就行。
代码实现
from collections import deque
dir=[(1,0),(-1,0),(0,1),(0,-1)]
def bfs(n,m, a, sx, sy, ex, ey):
queue = deque([(sx, sy, 0, 2)])
a[sx][sy] = 1
while queue:
x, y, turn, D = queue.popleft()
for dx,dy in dir:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < m and a[nx][ny] == 0:
if abs(dx)!=D:
turn+=1
D=abs(dx)
if (nx, ny) == (ex, ey):
return turn-1
queue.append((nx, ny, turn,D))
a[nx][ny] = 1
return -1
n,m=map(int, input().split())
a = [list(map(int, input().split())) for _ in range(n)]
sx, sy, ex, ey = map(int, input().split())
result = bfs(n,m, a, sx-1, sy-1, ex-1, ey-1)
print(result)
马的移动
算法思路
和求最短路径差不多,就是多了几个方向。就是得想办法把表示象棋坐标的字符串转换成矩阵中每个格子的下标。还要注意的就是如果起点和终点相同时的特判。
代码实现
from collections import deque
def dfs(posa,posb,grid):
queue=deque([(posa,0)])
dir=[(1,2),(2,1),(-1,2),(-2,1),(1,-2),(2,-1),(-1,-2),(-2,-1)]
while queue:
pos,step=queue.popleft()
for dx,dy in dir:
x,y=pos[0]+dx,pos[1]+dy
if 0<=x<8 and 0<=y<8 and grid[x][y]==0:
if (x,y)==posb:
return step+1
grid[x][y]=1
queue.append(((x,y),step+1))
return -1
while True:
try:
s = input()
posa=(ord(s[0])-97,int(s[1])-1)
posb=(ord(s[3])-97,int(s[4])-1)
s=s.replace(' ',' to ')
if posa==posb:
print(f'To get from {s} takes 0 knight moves.')
else:
grid=[[0]*8 for i in range(8)]
ans=dfs(posa,posb,grid)
print(f'To get from {s} takes {ans} knight moves.')
except EOFError:
break
图的遍历
算法思路
看注释
代码实现
from collections import deque
def bfs(n, matrix):
# 初始化一个布尔列表,用于标记每个节点是否已被访问
visited = [False] * n
# 初始化一个列表,用于记录遍历顺序
order = []
# 初始化一个队列,将起始节点(节点0)加入队列
queue = deque([0])
# 当队列不为空时
while queue:
# 从队列中弹出一个节点
vertex = queue.popleft()
# 如果当前节点未被访问
if not visited[vertex]:
# 将当前节点标记为已访问
visited[vertex] = True
# 将当前节点添加到遍历顺序列表中
order.append(vertex)
# 遍历所有可能的邻居节点
for neighbor in range(n):
# 如果邻居节点与当前节点有边相连且尚未访问过
if matrix[vertex][neighbor] == 1 and not visited[neighbor]:
# 将邻居节点加入队列
queue.append(neighbor)
# 返回遍历顺序列表
return order
# 读取图的节点数量
n = int(input())
# 读取邻接矩阵
matrix = [list(map(int, input().split())) for _ in range(n)]
# 调用广度优先搜索函数
order = bfs(n, matrix)
# 输出遍历顺序
print(*order)
过河卒
算法思路
一道经典的动态规划问题,B点坐标(bx,by)
,马的坐标(mx,my)
要求出不经过马控制位置,从(0,0)
到达B的走法数量。
创建一个长度为 by + 1
的一维列表dp
,用于动态规划计算从起点到每个位置的卒的数量。把横纵坐标都移2
格防止访问超限。起点为(2,2)
,这时候只有1
种走法。接下来,对每个位置进行遍历,dp[j]
表示到达第 j
行(即第 j
列上的任意位置)的路径数。如果当前位置可以走到马(mx, my)
的控制点,则不能继续走,对应的 f[j]
清零。否则,当前位置的可走路径数f[j]
等于前一个位置的可走路径数 f[j - 1]
。对于马的控制点,观察发现马的所有控制点就是马的曼哈顿距离为3的位置与切比雪夫距离为2的位置的交点。
设
A
(
x
1
,
y
1
)
,
B
(
x
2
,
y
2
)
A(x_1,y_1),B(x_2,y_2)
A(x1,y1),B(x2,y2)
曼哈顿距离
d
(
A
,
B
)
=
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
d(A,B) = |x_1-x_2|+|y_1-y_2|
d(A,B)=∣x1−x2∣+∣y1−y2∣
切比雪夫距离
d
(
A
,
B
)
=
m
a
x
(
∣
x
1
−
x
2
∣
,
∣
y
1
−
y
2
∣
)
d(A,B) = max(|x_1-x_2|,|y_1-y_2|)
d(A,B)=max(∣x1−x2∣,∣y1−y2∣)
代码实现
def check(x, y, mx, my):
if x == mx and y == my:
return True
return (abs(mx - x) + abs(my - y) == 3) and (max(abs(mx - x), abs(my - y)) == 2)
read=list(map(int, input().split()))
bx,by,mx,my=map(int,[x+2 for x in read])
dp = [0] * (by + 1)
dp[2] = 1
for i in range(2, bx + 1):
for j in range(2, by + 1):
if check(i, j, mx, my):
dp[j] = 0
else:
dp[j] += dp[j - 1]
print(dp[by])
个人练习赛记录
快乐数
代码实现
#include <bits/stdc++.h>
#define i64 long long
#define mod7 1000000007
using namespace std;
int main(){
int n, k;
cin>>n>>k;
vector<int> a(n);
for(auto& x:a){
cin>>x;
}
vector<i64>dp(k+10,0);
for(int i=0;i<n;i++){
vector<i64>ndp = dp;
for(int j=1;j*a[i]<=k;j++){
(ndp[j*a[i]]+=dp[j])%=mod7;
}
ndp[a[i]]++;
dp=ndp;
}
cout<<dp[k]<<endl;
return 0;
}
浙工院之笨笨
代码实现
n,m=map(int, input().split())
a=[0]+list(map(int, input().split()))
pre = [[0] * (n + 1) for _ in range(31)]
for i in range(1, n + 1):
x = a[i]
for j in range(31):
pre[j][i] = pre[j][i - 1] + (x % 2)
x >>= 1
result = []
for _ in range(m):
l,r=map(int, input().split())
s = ""
for i in range(31):
if pre[i][r] - pre[i][l - 1] == r-l+1:
s ='1'+s
else:
s ='0'+s
print(int(s, 2))
浙工院养奶牛
算法思路
找规律写出递推式
代码实现
year = 50
dp = [0,1,1,1]+[0]*(year-3)
for i in range(4, year + 1):
dp[i] = dp[i-1] + dp[i-3]
print(dp[year])
学长的因子
算法思路
看到这个题目我就想起来了上周学的约束个数的公式,但是超时了。。分解质因数要消耗太多不必要的时间空间,优化一下也是通过了,但是还是有更好的解法。首先找到两个整数 x 和 y的所有因子,然后计算这两个数的乘积,并查找哪些因子乘积能够整除这个乘积。具体步骤包括:提取 x和y 的因子,计算它们的乘积 xy,检查这些因子的所有可能组合是否可以整除 xy,最后统计满足条件的因子组合的数量。
代码实现
分解质因数解法
from collections import defaultdict
import math
def factor_count(x, y):
factors = defaultdict(int)
for n in (x, y):
num_factors = defaultdict(int)
while n % 2 == 0:
num_factors[2] += 1
n //= 2
for i in range(3, int(math.sqrt(n)) + 1, 2):
while n % i == 0:
num_factors[i] += 1
n //= i
if n > 2:
num_factors[n] += 1
for factor, count in num_factors.items():
factors[factor] += count
count = 1
for i in factors.values():
count *= (i + 1)
return count
x, y = map(int, input().split())
print(factor_count(x, y))
更快的方法
import math
x,y=map(int, input().split())
a = set()
b = set()
c = set()
for i in range(1, int(math.sqrt(x)) + 1):
if x % i == 0:
a.add(i)
a.add(x // i)
for i in range(1, int(math.sqrt(y)) + 1):
if y % i == 0:
b.add(i)
b.add(y // i)
nu = x * y
for i in a:
for j in b:
if nu % (i * j) == 0:
c.add(i * j)
print(len(c))
学长会旋转
算法思路
拿python超时,老老实实拿C++写了。只要把k模4,然后四种情况分类讨论就好了。
代码实现
#include <bits/stdc++.h>
using namespace std;
int main(){
int n, k;
cin >> n >> k;
k %= 4;
vector<vector<int>> mp(n+1,vector<int>(n+1));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>mp[i][j];
}
}
if(k==0){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<mp[i][j]<<" \n"[j==n-1];
}
}
}
else if(k==1){
for(int i=0;i<n;i++){
for(int j=n-1;j>=0;j--){
cout<<mp[j][i]<<" \n"[j==0];
}
}
}
else if(k==2){
for(int i=n-1;i>=0;i--){
for(int j=n-1;j>=0;j--){
cout<<mp[i][j]<<" \n"[j==0];
}
}
}
else{
for(int i=n-1;i>=0;i--){
for(int j=0;j<n;j++){
cout<<mp[j][i]<<" \n"[j==n-1];
}
}
}
return 0;
}
学长的数1
代码实现
n = int(input())
x = 1
while x <= n:
x *= 2
print(x-1 if x - 1 == n else x // 2 - 1)
学长的数2
代码实现
n = int(input())
k = 1
while (k << 1) <= (n + 1):
k <<= 1
nn = k * 2 - 1
for i in range(64):
if nn - (1 << i) <= n:
print(nn - (1 << i))
break
Atcoder Beginner Contest 367记录
A - Shout Everyday
算法思路
题目要求判断时间A是否不在时间B和时间C之间。要注意的是,如果B比C大说明C已经到了第二天,所以要做一下特殊处理。然后我用了一种直观的方法,就是用一个数组把两天的每个小时的数据存放起来,然后修改时间B和C之间的数据为1,最后看看A时间点的位置是不是0。
代码实现
A,B,C=map(int, input().split())
day=[0]*48
if C<B:
C+=24
A+=24
for i in range(B,C+1):
day[i]=1
if day[A]==0:
print("Yes")
else:
print("No")
B - Cut .0
算法思路
题目要求把小数部分多余的0给去掉,但是得考虑个情况,就是输入0.000输出0,这时候不仅要去掉最后的多的0还要把小数点去掉。
代码实现
n=input()
while n[-1]=='0':
n=n[:-1]
if n[-1]=='.':
n=n[:-1]
break
print(n)
C - Enumerate Sequences
算法思路
就是给你一个序列,每个位置上能填的数字是固定的可以随便选,输出所有的选择方案使得序列和为k的倍数。可以用 Python 中的 itertools.product
函数来生成笛卡尔积,并筛选出满足特定条件的序列。语法如下
# 示例 1:两个列表的笛卡尔积
list(itertools.product([1, 2], ['a', 'b']))
# 输出:[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
# 示例 2:一个列表和一个字符串的笛卡尔积
list(itertools.product([1, 2], 'xy'))
# 输出:[(1, 'x'), (1, 'y'), (2, 'x'), (2, 'y')]
# 示例 3:使用 repeat 参数
list(itertools.product([1, 2], repeat=2))
# 输出:[(1, 1), (1, 2), (2, 1), (2, 2)]
代码实现
import itertools
n, k = map(int, input().split())
r = list(map(int, input().split()))
choices = [list(range(1, r[i] + 1)) for i in range(n)]
all_sequences = []
for seq in itertools.product(*choices):
if sum(seq) % k == 0:
print(*seq)
算法学习笔记
广度优先搜索(BFS)
BFS 是一种图遍历方法,从源节点开始,逐层遍历图,分析与源节点直接相关的节点。然后,在 BFS 遍历中,必须移动到下一级邻居节点。
根据 BFS,您必须沿宽度方向遍历图:
- 首先,水平移动并访问当前层的所有节点。
- 继续下一层。
BFS如何运作
通常使用队列(Queue)数据结构来实现,因为BFS需要按照先进先出的顺序访问节点。
a. 选择一个起始节点,将其标记为已访问,并将其放入队列中(作为起始节点)。
b. 访问当前节点(可以是打印节点,或执行其他操作)。
c. 将所有未访问的相邻节点加入队列,并标记这些相邻节点为已访问,以避免重复访问。
d. 重复步骤 a-c,直到队列为空。
bfs解决的主要问题
- 最短路径问题:BFS可以用于查找从一个节点到另一个节点的最短路径,因为BFS按照层级遍历的方式逐步扩展搜索范围,可以保证先找到的路径是最短的。
- 连通性问题:BFS可以用于确定图中两个节点是否连通,即是否存在一条路径连接这两个节点。
- 拓扑排序问题:BFS可以用于进行拓扑排序,即对有向无环图(DAG)中的节点进行排序,使得所有的边从排在前面的节点指向排在后面的节点。
- 最小生成树问题:BFS可以用于查找最小生成树,如Prim算法中的一部分就是利用BFS来查找最小生成树。
- 能否到达问题:BFS可以用于判断从一个节点是否可以到达另一个节点,或者判断某个节点是否 可以到达图中的所有其他节点。
BFS模版
int check(int param) {
if (/* 满足条件 */) {
return 1;
}
return 0;
}
void bfs(int start) {
// 队列用于存储待处理的节点
queue<int> q;
// 标记数组用于记录节点是否已被访问
vector<bool> visited(/* 节点数量 */, false);
// 初始状态
q.push(start);
visited[start] = true;
while (!q.empty()) {
int current = q.front(); // 取出队首元素
q.pop();
// 判断当前状态是否符合要求
if (check(current)) {
// 执行相应操作
}
// 遍历当前节点的所有邻接节点
for (/* 每个邻接节点 */) {
int neighbor = /* 邻接节点 */;
if (!visited[neighbor]) {
// 标记已访问
visited[neighbor] = true;
// 将邻接节点加入队列
q.push(neighbor);
}
}
}
}
迷宫最短路径
题目
输入如下,第一行输入n表示一个n*n的迷宫。接下来是一个二维矩阵
- S表示起点
- E表示终点
- 0表示可以通过的通道
- 1表示墙
输出迷宫,用星号表示出最短路径
输入
S 0 0 1 1 1
1 1 0 1 0 1
1 0 0 0 0 1
1 1 1 1 0 1
1 0 0 1 0 0
1 1 1 1 1 E
输出
* * * 1 1 1
1 1 * 1 0 1
1 0 * * * 1
1 1 1 1 * 1
1 0 0 1 * *
1 1 1 1 1 *
代码实现
python
from collections import deque
def bfs_shortest_path(maze, start, end):
# 定义四个方向移动的偏移量,分别是上、下、左、右
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
rows, cols = len(maze), len(maze[0])
# 创建队列用于BFS,每个元素中包含了坐标和路径
queue = deque([(start, [start])])
visited = set()
while queue:
(x, y), path = queue.popleft()#从队列左侧拿出一个元素,解包成坐标和路径
visited.add((x, y))#把坐标加入到已经访问过的集合中
if (x, y) == end:
return path # 找到了最短路径
for dx, dy in directions:
#计算下一个坐标
new_x, new_y = x + dx, y + dy
# 判断下一个坐标是否在边界内,是否是空格,是否已经访问过
if 0 <= new_x < rows and 0 <= new_y < cols and maze[new_x][new_y] == '0' and (new_x, new_y) not in visited:
new_path = path + [(new_x, new_y)]#把当前坐标加入到路径中,作为下一个坐标的父节点
queue.append(((new_x, new_y), new_path))#把新的坐标和路径加入到队列中
return None # 没有找到路径
n=int(input())
#初始化迷宫
maze=[input().split() for i in range(n)]
start=()
end=()
for i in range(n):
for j in range(n):
if maze[i][j]=='S':
start=(i,j)
maze[i][j]='0'
elif maze[i][j]=='E':
end=(i,j)
maze[i][j]='0'
path = bfs_shortest_path(maze, start, end)
if path:
for x, y in path:
maze[x][y] = "*"
else:
print("No path found")
exit()
for row in maze:
print(*row)
动态规划
硬币问题
题目描述
今有面值为 1、5、11 元的硬币各无限枚。
想要凑出
n
n
n 元,问需要的最少硬币数量。
输入仅一行,一个正整数
n
n
n。
输出仅一行,一个正整数,表示需要的硬币个数。
样例输入
15
样例输出
3
算法思路
代码实现
用一个长度为(n+1)
的数组dp
记录下凑出i
元的时候至少需要多少硬币(dp[i]
)。凑出0元的时候需要0枚硬币,所以初始化dp[0] = 0
,而之后的每一项都设为无穷大。遍历dp
数组,对于每一个位置,尝试从1,5,11这三种面值的硬币选一枚拿取,如果当前想凑的钱数减去尝试拿取的硬币面额大于等于0,说明拿了这个硬币后,剩下需要凑的钱数所需的最少硬币数量一定是在这个位置之前就算过的,然后通过每次取最小值找出需要最少的硬币的数量是哪一种面值的硬币。
python
n = int(input())
coins = [1, 5, 11]
dp = [float('inf')] * (n + 1)
dp[0] = 0
for i in range(1, n + 1):
for coin in coins:
if i - coin >= 0:
dp[i] = min(dp[i], dp[i - coin] + 1)
print(dp[n])
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int coins[3] = {1, 5, 11};
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 3; j++) {
if (i - coins[j] >= 0) {
dp[i] = min(dp[i], dp[i - coins[j]] + 1);
}
}
}
cout << dp[n] << endl;
}
背包问题
因为之前写过关于背包问题的博客,所以直接放一个链接在这
【算法】背包问题(Knapsack problem)
(不过这个背包问题真是学了忘,忘了学,学了又忘T T,还得刷题啊)