文章目录
真题来源
牛客网-在线编程-全部编程题目,选择知识点
1.1 算法:查找
丰收(关注accumulate, bisect.bisect_left函数)
题目描述
又到了丰收的季节,恰逢小易去牛牛的果园里游玩。
牛牛常说他对整个果园的每个地方都了如指掌,小易不太相信,所以他想考考牛牛。
在果园里有N堆苹果,每堆苹果的数量为ai,小易希望知道从左往右数第x个苹果是属于哪一堆的。
牛牛觉得这个问题太简单,所以希望你来替他回答。
输入描述:
第一行一个数n(1 <= n <= 105)。
第二行n个数ai(1 <= ai <= 1000),表示从左往右数第i堆有多少苹果
第三行一个数m(1 <= m <= 105),表示有m次询问。
第四行m个数qi,表示小易希望知道第qi个苹果属于哪一堆。
输出描述:
m行,第i行输出第qi个苹果属于哪一堆。
示例1
输入
5
2 7 3 4 9
3
1 25 11
输出
1
5
3
代码
import sys
from itertools import accumulate
import bisect
N = int(input())
A = list(map(int, sys.stdin.readline().strip().split()))
M = int(input())
Q = list(map(int, sys.stdin.readline().strip().split()))
a = list(accumulate(A) )
for m in range(M):
print(bisect.bisect_left(a, Q[m]) +1)
二维数组中的查找(剑指Offer_编程题)
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
代码
从左下角开始向右上方寻找,是向右为变大,向上为变小;这样有分岔路,好查找。跟普通的从左上角往右下找是向右变大向下变大不一样。
另外的想法——先比较每一行的第一位、下一行的第一位和target的大小,以确定行:但是由于不具备单调,不能实现。
[[1,3,5],[2,4,6]] 不能通过3在1、2之间来找。
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
rows = len(array) - 1
cols = len(array[0]) - 1
i = rows
j = 0
while j <= cols and i >= 0:
if target < array[i][j]:
i -= 1
elif target > array[i][j]:
j += 1
else:
return True
return False
从1到n整数中1出现的次数(剑指Offer_编程题)
题目描述
求出1 ~ 13的整数中1出现的次数,并算出100 ~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
代码
class Solution:
def count_1(self,num):
count1=0
while(num!=0):
if num%10==1:
count1+=1
num/=10
return count1
def NumberOf1Between1AndN_Solution(self, n):
# write code here
res=0
for i in range(n+1):
res+=self.count_1(i)
return res
二分查找
题目描述
对于一个有序数组,我们通常采用二分查找的方式来定位某一元素,请编写二分查找的算法,在数组中查找指定元素。
给定一个整数数组A及它的大小n,同时给定要查找的元素val,请返回它在数组中的位置(从0开始),若不存在该元素,返回-1。若该元素出现多次,请返回第一次出现的位置。
测试样例:
[1,3,5,7,9],5,3
返回:1
代码
class BinarySearch:
def getPos(self, A, n, val):
left = 0
right = n-1
if n <=0:
return -1
while(left<=right):
mid = (left+right)/2
if A[mid]==val:
while A[mid] == A[mid-1]:
mid = mid-1
return mid
if A[mid]<val:
left = mid+1
if A[mid]>val:
right = mid-1
return -1
1.2 算法:排序
牛牛找工作
题目描述
为了找到自己满意的工作,牛牛收集了每种工作的难度和报酬。牛牛选工作的标准是在难度不超过自身能力值的情况下,牛牛选择报酬最高的工作。在牛牛选定了自己的工作后,牛牛的小伙伴们来找牛牛帮忙选工作,牛牛依然使用自己的标准来帮助小伙伴们。牛牛的小伙伴太多了,于是他只好把这个任务交给了你。
输入描述:
每个输入包含一个测试用例。
每个测试用例的第一行包含两个正整数,分别表示工作的数量N(N<=100000)和小伙伴的数量M(M<=100000)。
接下来的N行每行包含两个正整数,分别表示该项工作的难度Di(Di<=1000000000)和报酬Pi(Pi<=1000000000)。
接下来的一行包含M个正整数,分别表示M个小伙伴的能力值Ai(Ai<=1000000000)。
保证不存在两项工作的报酬相同。
输出描述:
对于每个小伙伴,在单独的一行输出一个正整数表示他能得到的最高报酬。一个工作可以被多个人选择。
示例1
输入
3 3
1 100
10 1000
1000000000 1001
9 10 1000000000
输出
100
1000
1001
代码
import sys
import bisect
lines = sys.stdin.readlines()
n, m = map(int, lines[0].strip().split())
jobs = dict()
for line in lines[1:-1]:
if not line.strip().split():
continue
a, b = map(int, line.strip().split())
jobs[a] = max(jobs.get(a, 0), b)
skills = map(int, lines[-1].strip().split())
sort_key = sorted(jobs.keys())
# 遍历把能力强但是工资低的换成工资高的选择
for ii in range(1, len(sort_key)):
if jobs[sort_key[ii]] < jobs[sort_key[ii-1]]:
jobs[sort_key[ii]] = jobs[sort_key[ii-1]]
for skill in skills:
if skill in jobs: # 如果改为 if skill in sort_key: 会时间过长,无法通过
print(jobs[skill])
else:
temp = bisect.bisect(sort_key, skill)
if temp==0:
print(0)
else:
print(jobs[sort_key[temp-1]])
1.3 算法:递归
1.4 算法:贪心
连续子数组最大和(贪心,动归)
题目描述
输入一个整形数组(可能有正数和负数),求数组中连续子数组(最少有一个元素)的最大和。要求时间复杂度为O(n)。
输入描述:
【重要】第一行为数组的长度N(N>=1)
接下来N行,每行一个数,代表数组的N个元素
输出描述:
最大和的结果
示例1
输入
8
1
-2
3
10
-4
7
2
-5
输出
18
说明
最大子数组为 3, 10, -4, 7, 2
代码
认为
d
p
[
i
]
dp[i]
dp[i] 是表示为
i
i
i 为结尾的最大子数组的和。
n = int(input())
l = list()
for i in range(n):
l.append(int(input()))
#
dp = [0] * n
for i in range(0,n,1):
if i==0 or dp[i-1]<=0:
dp[i] = l[i]
elif i>0 and dp[i-1]>0:
dp[i] = dp[i-1]+l[i]
print(max(dp))
划分数组,让最大的子数组最小
题目描述
shopee的零食柜,有着各式各样的零食,但是因为贪吃,小虾同学体重日益增加,终于被人叫为小胖了,他终于下定决心减肥了,他决定每天晚上去操场跑两圈,但是跑步太累人了,他想转移注意力,忘记痛苦,正在听着音乐的他,突然有个想法,他想跟着音乐的节奏来跑步,音乐有7种音符,对应的是1到7,那么他对应的步长就可以是1-7分米,这样的话他就可以转移注意力了,但是他想保持自己跑步的速度,在规定时间m分钟跑完。为了避免被累死,他需要规划他每分钟需要跑过的音符,这些音符的步长总和要尽量小。下面是小虾同学听的歌曲的音符,以及规定的时间,你能告诉他每分钟他应该跑多少步长?
输入描述:
输入的第一行输入 n(1 ≤ n ≤ 1000000,表示音符数),m(1<=m< 1000000, m <= n)组成,
第二行有 n 个数,表示每个音符(1<= f <= 7)
输出描述:
输出每分钟应该跑的步长
示例1
输入
8 5
6 5 6 7 6 6 3 1
输出
11
说明
6 | 5 6 | 7 | 6 | 6 3 1 为最优解
如果小于11,必然分段大于5
代码
import sys
line1 = list(map(int, sys.stdin.readline().strip().split() ))
line2 = list(map(int, sys.stdin.readline().strip().split() ))
n,m = line1
SUM = sum(line2)
MAX = max(line2)
left = MAX
right = SUM
while(left <= right):
mid = (left + right) // 2
count = 0
tmp = 0
for i in range(n):
tmp += line2[i]
if tmp > mid:
count += 1
tmp = line2[i]
if count + 1 <= m:
right = mid - 1
else:
left = mid + 1
print(mid)
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, m;
cin >> n >> m;
int x, left = 0, right = 0, mid;
vector<int> v;
while(cin >> x){
right += x; //全部数加和
left = max(left, x); // 取最大的数
v.push_back(x);
}
while(left <= right){
mid = left + (right - left) / 2;
int count = 0, temp = 0;
for(int i = 0; i < v.size(); i++){
temp += v[i]; //
if(temp > mid){
count++; // 只要再temp刚才加v[i]超过了mid,则计数加1
temp = v[i];
}
}
count+1 <= m ? right = mid-1 : left = mid + 1;
}
cout << mid << endl;
return 0;
}
1.5 算法:动态规划
回文数组(数组、动归)(搜狐)
题目描述
对于一个给定的正整数组成的数组 a[] ,如果将 a 倒序后数字的排列与 a 完全相同,我们称这个数组为“回文”的。
例如, [1, 2, 3, 2, 1] 的倒序是他自己,所以是一个回文的数组;而 [1, 2, 3, 1, 2] 的倒序是 [2, 1, 3, 2, 1] ,所以不是一个回文的数组。
对于任意一个正整数数组,如果我们向其中某些特定的位置插入一些正整数,那么我们总是能构造出一个回文的数组。
输入一个正整数组成的数组,要求你插入一些数字,使其变为回文的数组,且数组中所有数字的和尽可能小。输出这个插入后数组中元素的和。
例如,对于数组 [1, 2, 3, 1, 2] 我们可以插入两个 1 将其变为回文的数组 [1, 2, 1, 3, 1, 2, 1] ,这种变换方式数组的总和最小,为 11 ,所以输出为 11 。
输入描述:
输入数据由两行组成: 第一行包含一个正整数 L ,表示数组 a 的长度。 第二行包含 L 个正整数,表示数组 a 。 对于 40% 的数据: 1 < L <= 100 达成条件时需要插入的数字数量不多于 2 个。 对于 100% 的数据: 1 < L <= 1,000 0 < a[i] <= 1,000,000 达成条件时需要插入的数字数量没有限制。
输出描述:
输出一个整数,表示通过插入若干个正整数使数组 a 回文后,数组 a 的数字和的最小值。
示例1
输入
8
51 23 52 97 97 76 23 51
输出
598
代码
问题可以转换为求回文子序列的最大和,则最终最优解为2 * sum - dp[0][a.length - 1],sum为数组a所有元素的和。
参考
/**
* Dynamic Programming
*
* State:
* dp[i][j]: 表示a[i],...,a[j]中的回文子序列的最大和
*
* Initial State:
* dp[i][i] = a[i]
*
* State Transition:
* if (a[i] == a[j]) dp[i][j] = dp[i + 1][j - 1] + 2 * a[i]; 中间一段加上左右
* else dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); 取中间加右端或者左端
*
* @author wylu
*/
import sys
n = int(input())
lines = sys.stdin.readline().strip().split()
values = list(map(int, lines))
dp = [[0]*n for _ in range(n)]
s = 0
for i in range(n-1, -1, -1):
dp[i][i] = values[i]
s += values[i]
if i == n-1:
continue
for j in range(i+1, n):
if values[i] == values[j]:
dp[i][j] = dp[i+1][j-1] + values[i] * 2
else:
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
#
print(s*2 - dp[0][n-1])
连续
未归类
中位数
题目描述
小M给你一个长度为n的数组,我们定义median数为该数组从小到大排序后,下标为(n-1)/2的数字。下标从0开始,(n-1)/2表示整数除法,即向下取整。现在我们已经得到了一个初始的数组,我们希望这个数组的median数是一个给定数字x。所以我们需要加入一些数到数组中从而完成我们的目标。数组中的元素可以重复,请问,最少需要加入多少个数字才能达成这个目标。
输入描述:
第一行输入两个整数n x (1 <= n <= 500, 1 <= x <= 10^5)。
接下来一行有n个正整数表示初始的数组,用空格分开,范围是[1, 10^5]。
输出描述:
输出需要加入最少的元素个数才能够使得新数组的median数成为x。
示例1
输入
3 2
2 3 4
输出
1
说明
样例一中,加入1,得到1 2 3 4,那么median数的下标为(4 - 1)/2 = 1, median数为2。加一个数字就可以了。
示例2
输入
3 4
1 2 3
输出
4
说明
样例二中,加入4 5 6 7,得到1 2 3 4 5 6 7,median数为4。最少加4个数字。
代码
import sys
l1 = list(map(int, sys.stdin.readline().strip().split() ))
n, x = l1
l2 = list(map(int, sys.stdin.readline().strip().split() ))
e, r, l = 0,0,0
for i in range(len(l2)):
if l2[i]==x:
e += 1
elif l2[i] < x:
l += 1
else:
r += 1
#
m = (n-1)/2
if l <= m: # 给定值左边的个数 小于 给定值的位置;即左边的个数不够
if r < n-m: # 给定值右边的个数不够
print(0) # 数组中,给定值左边的不够,右边的也不够,所以中间的一些值都是给定值;故不需要添加数字
else: # 右边的个数多,所以左边添加个数
print(r - l - e) # 因为需要左边添加个数,所以将给定值当做左边的。这样可以尽可能少的添加
else: # 左边的个数多
print(l-r-e+1) # 需要添加右边的,所以将等于给定值的数当做右边的;同时由于m的位置是向下取整,因此右边需要多加1
统一字符串出现次数
通过键盘输入一串小写字母(a~z)组成的字符串。
请编写一个字符串归一化程序,统计字符串中相同字符出现的次数,并按字典序输出字符及其出现次数。
例如字符串"babcc"归一化后为"a1b2c2"
import sys
line1 = [each for each in sys.stdin.readline().strip()]
num = len(set(line1))
def rank(alist_raw):
alist = alist_raw.copy()
if len(alist) >= 2:
mid = alist[0]
left = []
right = []
alist.remove(mid)
for i in alist:
if i <= mid:
left.append(i)
else:
right.append(i)
return rank(left) + [mid] + rank(right)
else:
return alist
def statistic(alist_raw):
alist = alist_raw.copy()
num = 1
num_list = []
set_list = [alist[0]]
for i in range(1,len(alist)):
if alist[i]==alist[i-1]:
num += 1
else:
num_list.append(num)
set_list.append(alist[i])
num = 1
num_list.append(num)
return set_list, num_list
rank_alist = rank(line1)
result = []
set_list, num_list = statistic(rank_alist)
for i in range(num):
result.append(set_list[i])
result.append( num_list[i])
print("".join(str(i)for i in result))
题目2:找出数组中唯一一个出现奇数次的数字
题目——原型
由于任何两个相同的数(偶数次),异或处理之后,都为0;
0和任何数异或,都得到另外这个数;
加入不同的数字(奇数次)后,异或处理为本身。
代码
# 找出数组中唯一一个出现奇数次的数字
import sys
n = int(input())
line1 = [int(each) for each in sys.stdin.readline().strip().split()]
result = 0
line1_copy = line1.copy()
for i in range(1,len(line1)):
line1_copy[0] = line1_copy[0] ^ line1_copy[i]
print(line1_copy[0])
变形:找出数组中唯二出现奇数次的数字
一趟异或下来,最终的结果即为那两个奇数异或的结果;
如果其结果不为0,则说明至少存在这两个数中的某一个数的某一位不全为0
那么可以根据这个不同的位,将数组分成两个部分,再对这两个独立的部分,两天异或,就可以得到这两个奇数了
记得:取反加一得到补码(相反数)
# 找出数组中唯一一个出现奇数次的数字
##import sys
##n = int(input())
##line1 = [int(each) for each in sys.stdin.readline().strip().split()]
def findOddNum(line1_copy):
for i in range(1,len(line1_copy)):
line1_copy[0] = line1_copy[0] ^ line1_copy[i]
print(line1_copy[0])
def findOddTwiceNum(line2):
oneResult = 0
result = 0
for i in range(len(line2)):
result = result ^ line2[i]
oneRight = result & (~result + 1)
# 一个数和自己的相反数进行位与,得到的是原数中右起第一个为1,其余都为0的数
# 因为右起第一个为1的位,右边都是0(或者没有位),那么相反数的对应原数
# 0的位置,都是1。只要加1,就能够进位。一直到右起第一个1.
for i in range(len(line2)):
if line2[i] & oneRight != 0:
oneResult ^= line2[i]
# 由于相乘,只要符合条件的偶数次的数,肯定异或为0,忽略。剩下两个奇数次的数AB,
# 其中必定有一个A是和result有同一个最右起的第一个1。求与之后A这个数,
# 表现为非0,和之前的偶数次数求异或,得到A这个数本身。
# A是oneResult,另外一个B用result异或可得。
print(str(oneResult) + ' and ' + str(oneResult ^ result) )
line1 = [3,4,6,3,5,6,6,4,6,5,6] # 6
line1_copy = line1.copy()
findOddNum(line1_copy)
line2 = [3,3,5,6,5,4,7,7,4,4] # 4 and 6
findOddTwiceNum(line2)
找出数组中唯一一个出现偶数次的数字
通过删减替将word1改成word2的最小次数
def minDistance(word1, word2):
n1 = len(word1)
n2 = len(word2)
dp = []
for i in range(len(word1)+1):
dp.append(i)
for j in range(1,n2+1):
prev = dp[0]
dp[0] = j
for i in range(1,n1+1):
temp = dp[i]
if word1[i-1] == word2[j-1]:
dp[i] = prev
else:
dp[i] = 1 + min(prev, min(dp[i], dp[i-1]))
prev = temp
return dp[n1]
import sys
word1 = sys.stdin.readline().strip()
word2 = sys.stdin.readline().strip()
print(minDistance(word1,word2))