假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
城市数目,以及城市之间的距离
所有路径之中的最小值
4
0,10,15,20
10,0,35,25
15,35,0,30
20,25,30,0
80
第一行说明有4个城市;第2至5行说明一个城市与其他城市的距离,其中城市到其自身的距离为0,不可达的城市之间的距离为0。
数据较小,直接全排列枚举所有方案
from itertools import permutations
n = int(input())
ls = []
for i in range(n):
ls.append([int(x) for x in input().split(',')])
def tsp(n, ls):
cities = [i for i in range(n)]
res = float('inf')
for path in permutations(cities):
distance = 0
for i in range(n):
distance += ls[path[i - 1]][path[i]]
res = min(res, distance)
return res
print(tsp(n, ls))
对一个以XY轴表达的城市里,n个居民点散乱分布,居民点位置可以用坐标(x,y)表示,任意两点(x1,y1)(x2,y2)间距离可以用| x1-x1|+|y1-y2|度量,居民希望在城市中选择建立邮局的最佳位置,使得n个居民点到邮局的距离总和最小
居民点数目,以及居民点的坐标
最小的距离总和
3
3 3
1 6
4 9
9
贪心的思想,首先这个问题可以将横纵坐标分开考虑,对答案没影响,则问题转换为在一维坐标轴上面选择一个点,使得所有点到它的距离加起来最小。
放到最中间距离最小,因为假设有两个点AB,需要放置C点,如下:
A C B
A B C
显然放中间距离为AB,放两边距离为AB+BC/AC
n = int(input())
a, b = [], []
for i in range(n):
x, y = map(int, input().split())
a.append(x)
b.append(y)
## 排序
a.sort()
b.sort()
res = 0 ## 最优的选择就是放中间
for i in range(n):
res += abs(a[i] - a[n // 2]) + abs(b[i] - b[n // 2])
print(res)
给定k个排好序的序列s1,s2,…,sk,用2路合并算法将这k个序列合并成一个序列。假设所采用的2路合并算法合并两个长度分别为m和n的序列需要m+n-1次比较。试设计一个算法确定合并这个序列的最优合并顺序,使所需要的总比较次数最少。
序列数目,以及各个序列中的元素数目。
最少的总比较次数
4
5 12 11 2
52
贪心的思想:使用一个优先队列,每次从队列中取出两个最小的序列进行合并,再将得到的结果插入到优先队列中去。
import heapq
n = int(input())
ls = [int(x) for x in input().split(' ')]
priority_queue = []
for i in ls: heapq.heappush(priority_queue, i)
res = 0
while len(priority_queue) > 1:
a = heapq.heappop(priority_queue)
b = heapq.heappop(priority_queue)
res += a + b - 1
heapq.heappush(priority_queue, a + b)
print(res)
###【问题描述】
排序问题: 给定一个无序序列,采用”堆排序“方法对序列升序排序。
无序的整数序列
升序的整数序列
12,11,13, 5, 6,7
5, 6, 7,11,12,13
以逗号隔开的序列, 程序内需要转化为整数序列
堆的性质
显然这里是升序,因此需要小根堆
## 堆排序
def heapify(ls, n, i):
largest = i
l = 2 * i + 1
r = 2 * i + 2
if l < n and ls[i] < ls[l]:
largest = l
if r < n and ls[largest] < ls[r]:
largest = r
if largest != i:
ls[i], ls[largest] = ls[largest], ls[i]
heapify(ls, n, largest)
def heapSort(ls):
n = len(ls)
for i in range(n, -1, -1):
heapify(ls, n, i)
for i in range(n - 1, 0, -1):
ls[i], ls[0] = ls[0], ls[i]
heapify(ls, i, 0)
ls = [int(x) for x in input().split(",")]
heapSort(ls)
print(",".join(str(x) for x in ls))
苏科大的sb输入输出数据,题目描述的输入输出和答案数据全是乱的。
印刷电路板将布线区域划分成n*m个方格,精确的电路布线问题要求确定连接方格a的中点到方格b的中点的最短布线方案。在布线时,电路只能沿直线或直角布线。为了避免线路相交,已经布了线的方格不允许其他线穿过。对给定的电路板,找出最短的布线路径。
方阵的行数、列数、布线区域方阵(其中不能通过的地方输入1,能通过的地方输入0)、起点坐标、终点坐标
线路长度以及线路包含的方格坐标
6 6
0 0 1 0 0 0
0 0 1 1 0 0
0 0 0 0 1 0
0 0 0 1 1 0
1 0 0 0 1 0
1 1 1 0 0 0
1 2
4 3
4
1,2 > 2,2 > 3,2 > 4,2 > 4,3
第1行输入方阵的行数、列数
第2-7行输入方阵元素,不能通过的地方输入1,能通过的地方输入0
第8行输入起点坐标
第9行输入终点坐标
所有元素用空格分隔, 且坐标索引从1开始!!
思路很简单,就是BFS求最短路,只不过需要输出路径,那就记录前一个节点,最后回溯回去就可以
import queue
## 输入地图大小
n, m = map(int, input().split())
## 初始化地图
g = [[0 for _ in range(m + 1)] for _ in range(n + 1)]
for i in range(n):
g[i + 1] = [0]+[int(x) for x in input().split()]
## 输入起点和终点
start = tuple(map(int, input().split()))
end = tuple(map(int, input().split()))
## 初始化距离和状态数组,以及父节点数组
dist = [[0 for _ in range(m + 1)] for _ in range(n + 1)]
st = [[0 for _ in range(m + 1)] for _ in range(n + 1)]
father = [[(0, 0) for _ in range(m + 1)] for _ in range(n + 1)]
## BFS 队列初始化,加入起点
q = queue.Queue()
q.put(start)
st[start[0]][start[1]] = 1
flag = False
while not q.empty() and not flag:
## 弹出队首元素
t = q.get()
## 枚举四个方向
for i in range(4):
x = t[0] + [-1, 1, 0, 0][i]
y = t[1] + [0, 0, -1, 1][i]
## 判断是否越界或者障碍物已经被标记
if x < 1 or x > n or y < 1 or y > m:
continue
if g[x][y] == 0 and st[x][y] == 0:
## 如果可以走,则加入队列,更新距离,标记状态,记录父节点
q.put((x, y))
dist[x][y] = dist[t[0]][t[1]] + 1
st[x][y] = 1
father[x][y] = (t[0], t[1])
## 判断是否到达终点
if (x, y) == end:
flag = True
break
## 输出最短路距离
print(dist[end[0]][end[1]])
## 再次 BFS,打印路径
path = [end]
x = end[0]
y = end[1]
while (x, y) != start:
## 获取当前点的父节点
x, y = father[x][y]
path.append((x, y))
## 反转路径,并输出
path.reverse()
for i in range(len(path)):
if i == len(path) - 1:
print("{},{}".format(path[i][0], path[i][1]))
else:
print("{},{}".format(path[i][0], path[i][1]), end="-->")
其他三个测试数据如下:
由第一个数据看出
## 测试数据1
6 6
0 0 1 0 0 0
0 0 1 1 0 0
0 0 0 0 1 0
0 0 0 1 1 0
1 0 0 0 1 0
1 1 1 0 0 0
1 2
4 3
## 输出
4
1,2-->2,2-->3,2-->4,2-->4,3
## 测试数据2
6 6
1 1 1 1 0 0
0 0 0 0 1 0
0 0 0 1 1 0
1 0 0 0 0 0
1 1 1 0 0 0
3 1
3 6
## 输出
9
3,1-->4,1-->4,2-->5,2-->5,3-->5,4-->5,5-->5,6-->4,6-->3,6
## 测试数据3
4 4
0 1 0 0
0 1 0 1
0 0 0 0
0 0 1 1
1 1
1 4
## 输出
7
1,1-->2,1-->3,1-->3,2-->3,3-->2,3-->1,3-->1,4
第二个测试数据最后莫名其妙加一些空格
一辆汽车加满油后可以行驶n千米。旅途中有k个加油站。若要使沿途的加油次数最少,设计一个有效的算法,采用贪心算法,编程计算并输出最少加油次数,以及指出应在那些加油站停靠加油。
两行标准输入,数字通过空格分隔
将编程计算出的最少加油次数,以及哪些加油站,输出到文件ouput.txt。如果无法到达目的地,则输出“No Solution”。
7 7
1 2 3 4 5 1 6 6
4
3 4 6 7
第一行标准输入有2个正整数n和k,表示汽车加满油后可行驶n km,且旅途中有k个加油站。
第二行标准输入有k+1个整数,表示第k个加油站与第k-1个加油站之间的距离。第0个加油站表示出发地,汽车已加满油。第k+1个加油站表示目的地。
贪心,如果序列中有一个大于n了,那么说明肯定到不了,否则一定有解,因为最坏的情况就是每站都加
可以开一个变量s,记录当前已经跑了多远了,如果加上下一站还够的话,就继续跑,否则就在本站加油
## 输入s,去除末尾空格
s = input().rstrip()
n, k = map(int, s.split(" "))
ls = [0] + [int(x) for x in input().split(" ")]
## 汽车加油问题
flag = True
for i in ls:
if i > n:
flag = False
break
if flag:
res = 0
s = 0
ans = []
for i in range(k + 1):
if s + ls[i + 1] > n:
ans.append(i)
res += 1
s = 0
s += ls[i + 1]
print(res)
for i in ans:
print(i, end=" ")
else:
print("No Solution")
给定一个无向图G=(V,E),若U为V子集,请对任意的顶点u, v为U的元素,有边(u,v)为E元素,则称U为G的一个完全子图。G的完全子图U是一个团,当且仅当U不包含在G的更大的完全子图中。G的最大团则指包含定点数最多的团。对给定的无向图,找出最大团中顶点的个数。
G的邻接矩阵
第一行输出最大团顶点个数,第二行输出最大团中的顶点
0,1,1,0,0
1,0,1,1,1
1,1,0,1,1
0,1,1,0,1
0,1,1,1,0
最大团顶点个数: 4
最大团为: [0, 1, 1, 1, 1]
输入的邻接矩阵表明图中有5个顶点,矩阵元素为1,则行、列对应的顶点有边相连,否则没有边相连。矩阵元素之间通过逗号隔开。举例:假设5个顶点分别为ABCDE,那么A与B、C相连。最终求得,最大团中包含4个节点,为BCDE。
任意两点都恰有一条边相连的图,n个顶点的图中有 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1) 条边。
任意两点都恰好有一条边相连的子图,也叫团。
每次看当前点u如果他和最大团中的点没有边的话就不加,如果都有边的话,那么就加进去。
graph = []
while True:
try:
graph.append(list(map(int, input().split(","))))
except:
break
n = len(graph[0])
## 用于存储最大团的点集
ans = [0 for _ in range(n)]
## 用于存储当前团的点集
st = [0 for _ in range(n)]
res = 1
def dfs(u, num):
global res, ans, n
if u == n:
if num > res:
res = num
ans = st[:]
return
flag = True
for i in range(n):
if st[i] == 1 and graph[u][i] == 0:
flag = False
break
if flag:
st[u] = 1
dfs(u + 1, num + 1)
st[u] = 0
dfs(u + 1, num)
else:
dfs(u + 1, num)
dfs(0, 0)
print("最大团顶点个数: {}".format(res))
print("最大团为: {}".format(ans))
## Test1
0, 1, 1, 0, 0
1, 0, 1, 1, 1
1, 1, 0, 1, 1
0, 1, 1, 0, 1
0, 1, 1, 1, 0
## Result1
最大团顶点个数: 4
最大团为: [0, 1, 1, 1, 1]
## Test2
0, 1, 1, 1
1, 0, 1, 1
1, 1, 0, 1
1, 1, 1, 0
## Result2
最大团顶点个数: 4
最大团为: [1, 1, 1, 1]
## Test3
0, 1, 0, 0, 0, 1
1, 0, 1, 1, 1, 1
0, 1, 0, 1, 1, 0
0, 1, 1, 0, 1, 0
0, 1, 1, 1, 0, 1
1, 1, 0, 0, 1, 0
## Result3
最大团顶点个数: 4
最大团为: [0, 1, 1, 1, 1, 0]
若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。如果给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。现给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},要求使用动态规划算法思想,找出X和Y的最长公共子序列。
输入以空格分割的字符,第一行对应第一条序列,第二行对应第二条序列。
字符数组形式的最长公共子序列。
z x y
x y y
['x', 'y']
第一行输入为第一条序列,包含z x y三个字符;输出为最长公共子序列,包含两个字符,以数组形式输出。
动态规划:
dp[i][j]
表示a的[0i]和b的[0j]的最长公共子序列的长度的最大值dp[i][j]=dp[i-1][j-1]+1
dp[i][j]=max(dp[i-1][j],dp[i][j-1])
a = input().split()
b = input().split()
n, m = len(a), len(b)
## dp[i][j]表示a[0:i]和b[0:j]的最长公共子序列长度
dp = [[0 for i in range(m + 1)] for j in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, m + 1):
if a[i - 1] == b[j - 1]: ## a[i - 1]和b[j - 1]相等,那么dp[i][j]就是dp[i - 1][j - 1] + 1
dp[i][j] = dp[i - 1][j - 1] + 1
else: ## a[i - 1]和b[j - 1]不相等,那么dp[i][j]就是dp[i - 1][j]和dp[i][j - 1]的最大值
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
## 回溯 输出最长公共子序列
res = []
i = n
j = m
while i > 0 and j > 0:
if a[i - 1] == b[j - 1]: ## a[i - 1]和b[j - 1]相等,那么a[i - 1]就是最长公共子序列的一个元素
res.append(a[i - 1])
i -= 1
j -= 1
elif dp[i - 1][j] > dp[i][j - 1]: ## a[i - 1]和b[j - 1]不相等,那么dp[i][j]就是dp[i - 1][j]和dp[i][j - 1]的最大值
i -= 1
else:
j -= 1
res.reverse()
print(res)
给定一个2k×2k的棋盘(具体图例见教材),有一个特殊棋格,拥有一个特殊棋格的棋盘称为特殊棋盘。现要用四种L型骨牌(具体图例见教材)覆盖特殊棋盘上除特殊棋格外的全部棋格,不能重叠,找出覆盖方案。
在屏幕上输入棋盘大小及特殊方格所在行号和列号
输出使用L型骨牌进行棋盘覆盖结果
2
1 2
2 -1 3 3
2 2 1 3
4 1 1 5
4 4 5 5
输入:第一行输入整数k表示棋盘大小为2的k次幂,若k为2,则棋盘大小为4行4列;第二行输入特殊方格所在的行号和列号,以空格分隔
输出:使用L型骨牌进行棋盘覆盖结果,由3个相同的数表示同一个L型骨牌,不同的骨牌用不同的数字表示。数字的大小表示棋盘覆盖的顺序。特殊方格在棋盘的第1行第2列,用-1表示。各整数间以空格分隔。
使用数学归纳法可知这个问题一定有解,因此可以将整个棋盘分为四个部分,因为有障碍物的那个部分一定是有解的,我们可以把剩下的三个棋盘每个棋盘在切开的位置都看成有障碍物,因此剩下的三个棋盘就都有解了,在3个看成障碍物的地方可以使用一个L型进行覆盖
k = int(input())
obstacle = tuple(map(int, input().split()))
board = [[0 for _ in range(2 ** k)] for _ in range(2 ** k)]
board[obstacle[0]-1][obstacle[1]-1] = -1
## 每个放置的L都需要自己的编号
cnt = 1
def chessboard(board, tr, tc, dr, dc, size):
global cnt
if size == 1:
return
s = size // 2
t = cnt
cnt += 1
## 覆盖左上角子棋盘
if dr < tr + s and dc < tc + s:
chessboard(board, tr, tc, dr, dc, s)
else:
board[tr + s - 1][tc + s - 1] = t
chessboard(board, tr, tc, tr + s - 1, tc + s - 1, s)
## 覆盖右上角子棋盘
if dr < tr + s and dc >= tc + s:
chessboard(board, tr, tc + s, dr, dc, s)
else:
board[tr + s - 1][tc + s] = t
chessboard(board, tr, tc + s, tr + s - 1, tc + s, s)
## 覆盖左下角子棋盘
if dr >= tr + s and dc < tc + s:
chessboard(board, tr + s, tc, dr, dc, s)
else:
board[tr + s][tc + s - 1] = t
chessboard(board, tr + s, tc, tr + s, tc + s - 1, s)
## 覆盖右下角子棋盘
if dr >= tr + s and dc >= tc + s:
chessboard(board, tr + s, tc + s, dr, dc, s)
else:
board[tr + s][tc + s] = t
chessboard(board, tr + s, tc + s, tr + s, tc + s, s)
chessboard(board, 0, 0, obstacle[0]-1, obstacle[1]-1, 2 ** k)
for i in range(2 ** k):
for j in range(2 ** k):
print(board[i][j], end=' ')
print()