特殊的序列
时间限制:C/C++语言 2000MS;其他语言 4000MS
内存限制:C/C++语言 131072KB;其他语言 655360KB
题目描述:
某个序列的最长不下降子序列的定义为将这个序列去除最少的数,使得剩下的每一个数都大于等于他自身前面的数。比如,1,0,0,1,1的最长不下降子序列为0,0,1,1,其中去除了第一个1,剩下的数0,0,1,1后面的数都大于等于前面的数。
现在有一个特殊的序列,这个序列中所有的数都是0或者1。你需要按照题目所给的顺序完成两种任务:
1.将某段区间的0变为1,1变为0
2.询问整段序列的最长不下降子序列长度。
每一个操作进行后都会对序列造成改变,这意味着整个序列会不停的发生变化。
输入
第一行2个数n,m,代表序列长度和询问次数
第二行n个数字,中间没有空格。每个数字为0或者1,第 i 个数代表序列中第i个数的大小
接下来m行,每行一个询问。其中,两个操作的询问方式如下:
1.c x y将区间[x,y]的0变为1,1变为0。
2.q 询问整段序列的最长不下降子序列长度。
注意,序列的第一个位置从开始标号,意思为整个序列的下标为1,2…n
1≤n≤100000 , 1≤m≤100000 , 1≤x≤y≤n
输出
对于第二种操作:q类型询问,输出整段序列的最长不下降子序列长度。
样例输入
5 5
10011
q
c 1 5
q
c 1 3
q
样例输出
4
3
4
提示
样例解释
1.第一次询问,原序列为10011,答案为0011
2.第二次修改,原序列为10011,修改后为01100
3.第三次询问,原序列为01100,答案为011或者000
4.第四次修改,原序列为01100,修改后为10000
5.第五次询问,原序列为10000,答案为0000
解法:
首先,先说明一下什么叫做最长不下降子序列,就是从原序列中挑出某些个0和1,保留这些数字的相对位置构成一个不下降的子串,要求子串长度最大。
我的做法是:
- 先找出全0(全1)这样的不下降子串的长度,作为一个备选项;
- 然后找出连续的不下降子串,记录开始位置和结束位置,看在开始位置前有多少个0,这些0可以加入连续不下降子串构成最长不下降子序列,同理看在结束位置后还有多少个1;
- 比较前两步的结果,取最大
from collections import Counter
def is_not_dec(l):
cnt = Counter(l)
mmax = max(cnt[0], cnt[1])
l.append('$')
s, e, sub_max = 0, 0, 0
for i in range(1, len(l)):
if l[i]=='$' or l[i-1] > l[i]:
if e - s + 1 >= sub_max:
m_s, m_e = s, e
sub_max = e - s + 1
s = i
else:
e = i
cnt_left, cnt_right = Counter(l[:m_s]), Counter(l[e + 1:])
mmax = max(mmax, sub_max + cnt_left[0] + cnt_right[1])
return mmax
n, m = map(int, input().split())
s, l = input(), []
for i in s:
l.append(int(i))
for i in range(m):
cmd = input()
if cmd[0] == 'q':
print(is_not_dec(l))
else:
x, y = map(int, cmd[1:].
split())
for i in range(x - 1, y):
l[i] = abs(l[i] - 1)
时间限制:C/C++语言 1000MS;其他语言 3000MS
内存限制:C/C++语言 65536KB;其他语言 589824KB
题目描述:
晨晨是个爱跑步的孩子,这一天,他准备跑正好k米。他所在的城市的道路可以看做n个点,m条无向边组成的图,每条边有一个固定的长度。
晨晨有强迫症,他跑步前往一个目的地一定要走最短路(当然有多条最短路就可以随意选择了)。
晨晨希望知道,他正好跑k米能走到的目的地的个数。注意,目的地可能在图中的点和边上,且该目的地距离晨晨的起点的最短路正好k米。
若k大于所有路径之和自然不存在这样的目的地,输出结果自然为0。
输入
第一行输入三个数,n,m,s代表图中的点数,边数,以及晨晨的起点的编号
接下来m行,每行3个数u,v,w描述一条无向边,代表点u到点v有一条无向边,长度为w。
接下来一行一个数k,描述晨晨希望跑的距离。
输出
输出一个数,代表不同的目的地个数。
样例输入
3 3 1
1 2 2
2 3 3
1 3 4
4
样例输出
2
提示
对于30%的数据 n,m≤100
对于100%的数据 0≤n,m≤100000 , 0≤w≤1000 , 1≤k≤10^9 , 1≤u,v≤n
例如:晨晨希望跑4米,他可以沿着第三条边直接跑向3号节点,此时跑步距离为4。他也可以先跑第一条边2米,再跑第二条边2米,停在第二条边的中间2/3的位置。可以证明,这两个目的地到1号节点的最短路都为4
解法:
目的地只有两种取法:(1)某个顶点(2)某段路中
故,我们先通过dijkstra找出从出发点到各顶点的最短距离,如果该距离等于要跑的距离,那么该顶点就是一个目的地;如果该距离小于要跑的距离,我们就先以最短距离跑到当前顶点,再看当前顶点的其余路径是否可构成取法(2)的情况
n, m, s = map(int, input().split())
s = s - 1
# initialize the graph
g = [[2000 for j in range(n)] for i in range(n)]
for i in range(m):
u, v, w = map(int, input().split())
u, v = u - 1, v - 1
g[u][v] = w
g[v][u] = w
# dijkstra
dis = g[s]
nn = len(dis)
flag = [0 for i in range(nn)]
flag[s] = 1
for i in range(nn - 1):
mmin = 2000
for j in range(nn):
if flag[j] == 0 and dis[j] < mmin:
mmin = dis[j]
u = j
flag[u] = 1
for v in range(nn):
if flag[v] == 0 and g[u][v] < 2000:
if dis[v] > dis[u] + g[u][v]:
dis[v] = dis[u] + g[u][v]
miles = int(input())
res = []
for i in range(len(dis)):
if dis[i] == miles:
res.append(str(i))
if dis[i] < miles:
for j in range(len(g[i])):
if g[i][j] != 2000 and g[i][j] + dis[i] > miles:
tmp = str(i) + str(j)
if tmp not in res:
res.append(tmp)
print(len(res))
时间限制:C/C++语言 1000MS;其他语言 3000MS
内存限制:C/C++语言 65536KB;其他语言 589824KB
题目描述:
现在一共有n个任务可以完成。对于每个任务,都有k个子任务可以做。并且第 i 个子任务需要花费的时间是 ti 。我们可以认为一个子任务需要的时间只和这个子任务是第几个子任务有关,而不和这是属于哪个任务有关。也就是说n个任务的第 i 个子任务需要的时间都是一样的。
每个任务都只可以完成一次,同时每个子任务也只能完成一次,任何任务都不能重复完成。
每当你完成一个子任务你会获得p分,而当你完成一个任务的k个子任务后,你会获得额外的q分,也就是说你会获得pk+q分。
现在你一共有m的时间,你需要求出最大的得分。
输入
第一行三个整数n,k,m。(1≤n≤100),(1≤k≤100),(0≤m≤2e9)
第二行两个整数p,q。(1≤p,q≤100)
第三行k个整数表示每个子任务需要的时间。(1≤ ti≤1e6)
输出
输出在m的时间内能获得的最大得分。
样例输入
3 2 8
3 1
9 5
样例输出
3
提示
输入样例
2 2 3
1 2
1 1
输出样例
5
解法:
视作背包问题,稍微有一个问题,就是dp矩阵的大小——我把每项任务的子任务都给了一行,当n和k取值较大时dp矩阵将变得十分巨大
n, k, m = map(int, input().split())
score = [[0 for j in range(m + 1)] for j in range(n * (k + 1) + 1)]
p, q = map(int, input().split())
v = []
for i in range(k):
v.append(p)
v.append(k * p + q)
v.insert(0, 0)
w = list(map(int, input().split()))
k_w = 0
for i in w:
k_w += i
w.append(k_w)
w.insert(0, 0)
res = 0
for i in range(1, n * (k + 1) + 1):
for j in range(1, m + 1):
ii = i % (k + 1) if i % ( k + 1) != 0 else k + 1
if j >= w[ii]:
score[i][j] = max(score[i - 1][j], score[i - 1][j - w[ii]] + v[ii])
res = max(res, score[i][j])
else:
score[i][j] = score[i - 1][j]
res = max(res, score[i][j])
print(res)
最长上升子串构造
时间限制:C/C++语言 1000MS;其他语言 3000MS
内存限制:C/C++语言 65536KB;其他语言 589824KB
题目描述:
给出一个长度为n的由正整数构成的序列,你需要从中删除一个正整数,很显然你有很多种删除方式,你需要对删除这个正整数以后的序列求其最长上升子串,请问在所有删除方案中,最长的上升子串长度是多少。
这里给出最长上升子串的定义:即对于序列中连续的若干个正整数,满足a_{i+1}>a_i,则称这连续的若干个整数构成的子串为上升子串,在所有的上升子串中,长度最长的称为最长上升子串。
输入
输入第一行仅包含一个正整数n,表示给出的序列的长度。(1<=n<=100000)
接下来一行有n个正整数,即这个序列,中间用空格隔开。(1<=a_i<=100000)
输出
输出仅包含一个正整数,即删除一个数字之后的最长上升子串长度。
样例输入
5
2 1 3 2 5
样例输出
3
解法:
按照一趟遍历的方法搜寻列表中所有的升序子串,维护一个列表存放每个升序子串的开始位置和结束位置,列表大小为2,当列表两个升序子串中间间隔超过一个元素时,则抛出第一个字串;如刚好只间隔一个元素,则判断前后两个子串能否通过删除中间间隔的元素构成一个更长的升序子串
def is_up(l):
s, e, mmax, res = 0, 0, 0, []
l.append('$')
for i in range(1, len(l)):
if l[i] == '$' or l[i - 1] >= l[i]:
if s != e:
res.append((s, e))
mmax = max(mmax, e - s + 1)
if len(res) == 2:
s_tmp, e_tmp = res.pop(0)
if e_tmp + 1 == s:
if l[e_tmp] >= l[s] and l[e_tmp] < l[s + 1]:
mmax = max(mmax, e_tmp - s_tmp + e - s +1)
s = i
e = s
else:
e = i
return(mmax)
n = int(input())
l = list(map(int, input().split()))
mmax = is_up(l)
print(mmax)