アルゴリズム小規模トライアル(夏合宿の準備でACWINGをもう一度やり直すのとほぼ同等)

1. 最長の非反復部分文字列

この質問の具体的な意味を説明する必要はありませんが、ここでは 2 つのアルゴリズムを紹介します。

1) 暴力的な捜索

マシンが十分に速い限り、Baosou で解決できないことは何もありません ^ ^ (冗談ですが、
非常に簡単です。長さと左側の境界をトラバースするだけです。これについては何も言う必要はありません。

s = input()
n = len(s)
def solve(s):  # 判断字符串是否有重复,返回True 代表没重复
    charstr = set()
    for ch in s:
        if ch in charstr:
            return False
        charstr.add(ch)
    return True

res = 1
for maxl in range(2,n + 1):   # 字符串长度
    for i in range(n - maxl + 1):  # 左边界
        s2 = s[i : i + maxl]
        if solve(s2): res = maxl  # 我们的maxl是递增的,不用比较

2) スライディングウィンドウ

暴力的な検索は実際に多くのリソースを浪費していることがよくわかります。たとえば、部分列 1 ~ 5 に重複がある場合、部分列 1 ~ 6 を検索する必要はありません。そこで、スライディング ウィンドウというアイデアが生まれました。存在します。
左と右の 2 つのポインタがあります。右が最初に移動します。トラバースされた要素がセットにない場合は、要素を追加し、レンズ + 1 を加えます。そうでない場合は、左に移動し、左に削除します。要素が完了するまで要素に追加します。右ポインタが指すセットがセットにありません

具体的なコードは以下の通り

s = input()
n = len(s)
left, right, lens, maxlen = 0, 0, 0, 0
strset = set()
while right < n:
    if s[right] not in strset:
        strset.add(s[right])
        right += 1
        lens += 1
        maxlen = max(lens,maxlen)
    else:
        while (s[right] in strset):
            strset.remove(s[left])
            left += 1
            lens -= 1
print(maxlen)

2.クイックキュー

クイックソートの考え方は非常に簡単です。数値を取得し、数値の左側がその数値より小さく、数値の右側がそれより大きくなるように、その数値が配置されるべき位置を見つけるだけです。それ、分割して征服する (この数字は「彼がどこにいようと関係ない」に変わりますか)

特定のコード

n = int(input())
a = [int(x) for x in input().split()]
def quicksort(a,l,r):
    if l >= r : return
    x = a[l]
    i, j = l - 1, r + 1
    while (i < j):
        i += 1
        j -= 1
        while (a[i] < x):
            i += 1
        while (a[j] > x):
            j -= 1
        if (i < j): a[i], a[j] = a[j], a[i]
    quicksort(a,l,j)
    quicksort(a,j + 1,r)

quicksort(a,0,n - 1)
print(' '.join(map(str,a)))

3. マージソート

また、分割統治の考え方も採用されており、
ここに画像の説明を挿入します
ローカル順序付け --> グローバル順序付けという分割統治のソートを図で示すことができます。

具体的なコードは以下の通り

n = int(input())
a = [int(x) for x in input().split()]
def merge_sort(a,l,r):
    if l >= r : return
    mid = (l + r) // 2
    merge_sort(a, l, mid)
    merge_sort(a, mid + 1, r)

    temp = []
    i,j = l,mid + 1
    while (i <= mid and j <= r) :
        if a[i] < a[j]:
            temp.append(a[i])
            i += 1
        else:
            temp.append(a[j])
            j += 1
    
    if i <= mid:
        temp += a[i : mid + 1]
    else:
        temp += a[j : r + 1]
    a[l : r + 1] = temp[:]

merge_sort(a, 0, n - 1)
print(' '.join(map(str,a)))

4.整数二分

実際には 2 つのテンプレートがあります。具体的な質問に適用してみましょう。1
つ目は、mid = (l + r) // 2です。このとき、a[mid] >=kr = mid を確認する必要があります。2 つ目は、次のとおりです
。 is mid = (l + r). + 1) // 2. このとき、a[mid] <=kl = Mid
else に注目してください。 注意だけすれば、r は常に l - 1 になります。

具体的なコードは以下の通り

## 整数二分,找到值在列表中的范围
# 第一行数n 和要查询的个数
# 第二行是单调递增的数组
# 其余后面几行是查询的数
# 浮点数二分没有那么复杂,没有+1 -1
n ,q = map(int, input().split())
a = [int(x) for x in input().split()]
for i in range(q):
    l ,r = 0, n - 1
    k = int(input())  # 要查询的数
    # 第一种模板
    while (l < r):          
        mid = (l + r) // 2
        if (a[mid] >= k):
            r = mid 
        else:
            l = mid + 1

    if a[l] != k : print('-1 1')
    else: 
        print(l, end = ' ')  # l相当于左边界
        print(r)
    # 第二种模板
    l ,r = 0, n - 1
    while (l < r):
        mid = (l + r + 1) // 2
        if (a[mid] <= k):
            l = mid
        else:
            r = mid - 1

    print(l)   # 这个l相当于右边界

5. 1次元プレフィックス合計

これについては何も言うことはありませんが、l から r までの配列の合計をすばやく求めるだけです #
a[l] +...+ a[r] を見つけると非常に便利です (添え字は 1
n から始まります)。 m = map(int,input( ).split())
a = [0] + [int(x) for x in input().split()]
s = [0] * (n + 1)
for i in範囲(1,n + 1) :
s[i] = s[i - 1] + a[i]

for i in range(m):  # m次查询
    l, r = map(int,input().split())
    print(s[r] - s[l - 1])

---------------1つずつ練習するのは遅すぎることに気づいたので、グラフ理論とDPに直接切り替えて、この2つの部分に焦点を当てます。

まずは短い部分から
ここに画像の説明を挿入します

6.ディクストラ

これは実際には非常に単純です。熟練が必要です。今、書き直すのは少し難しいです。
ダイクストラの原理は非常に単純です。毎回 n (点の数) 回繰り返し、そのたびに原点からの距離が最も短い点を使用して、原点から他の点までの距離を更新します。ここでは具体的な証明については詳しく説明しません。時間計算量は n^2 です。ヒープの最適化があるようです。 mlogn の複雑さを実現できるメソッド。
ここに画像の説明を挿入します

具体的なコードは以下の通り

N,null = 510,0x3f3f3f3f


def dijkstra():
    dist[1] = 0
    
    for _ in range(n):  # 更新n次 因为有n个点,每次确定一个
        t = -1
        # 找出没有被确认的最短路径的点集合中离源点最近的点
        for j in range(1,n+1):
            if (not st[j] and (t == -1 or dist[t] > dist[j])):  # 他是第一个点 或者后面的点距离原点的距离比他小
                t = j
        st[t] = True
        for j in range(1,n+1):  # 用t去更新 其实只要更新不确定的,但为了代码方便就全部一起更新
            dist[j] = min(dist[j],dist[t] + g[t][j])
    
    if dist[n] == null : return -1
    else: return dist[n]


if __name__ == '__main__':
    g = [[null]*N for _ in range(N)]  # 稠密图用邻接矩阵
    dist,st = [null]*N,[False]*N      # dist用于存储每个点到起点的距离   st用于表示当前点已经确定最短了
    n,m = map(int,input().split())
    
    for _ in range(m):
        x,y,z = map(int,input().split())
        g[x][y] = min(g[x][y],z)  # 可能有重边 取最小的
    
    print(dijkstra())

7.ベルマンフォード

このアルゴリズムは、エッジ情報に基づいて複数回更新されます。最終的に、1 から n までの最短経路を見つけるには、n (点の数) 回更新する必要があります。このアルゴリズムの利点は次のとおりです: 1. 負のエッジが許可される, 2
. 通過できるエッジの最大数を 1 から n に制限できます (質問を見てください)

ここに画像の説明を挿入します

具体的なコードは以下の通り

N, null = 100010, 0x3f3f3f3f
import copy
def bellman_ford():
    dist[1] = 0
    for _ in range(k):   # 遍历k次,因为有限制
        last = copy.deepcopy(dist) # 复制一下,只有限制边数才用到,怕会发生串联
        for j in range(m):  # 遍历遍历都更新所有的边
            a, b, c = edges[j]
            dist[b] = min(dist[b], last[a] + c)

    if dist[n] > null / 2: print('impossibile')
    else: print(dist[n])




if __name__ == '__main__':
    n, m, k = map(int,input().split())
    dist = [null] * N
    edges = []
    for _ in range(m):
        x, y, z = map(int,input().split())
        edges.append((x, y, z))
    bellman_ford()

8.spfa

このアルゴリズムは実際には bellman-ford を改良したものであり、最悪の場合、時間計算量は bellman-ford アルゴリズムと同じになります (ほとんどの競技はこれで行き詰まってしまうようです)。他のポイントも更新できます。具体的にコードを見てください。私はストレージにリンク リストを使用するのがあまり好きではありません。ポイントが多すぎる場合、隣接行列は 100000*100000 にする必要があり、明らかに大きすぎます。 2 つのリストを使用して、ポイントの接続とポイントの重みをそれぞれ保存することを検討できます (リンク リストを使用しない場合)。実装は簡単です。ここでは隣接行列のみを使用します。

ここに画像の説明を挿入します

具体的なコードは以下の通り

##我还是不喜欢用链表存,我感觉有点麻烦
from collections import deque


def spfa():
    dist[1] = 0
    q = deque([1])
    while q:
        t = q.popleft()    # 弹出开头的元素
        st[t] = False      # 他就不在队列中了
        for i in range(1, n + 1):   # 这一步其实是要找到和t相连的结点,但是我没什么好的办法,只能遍历所有的
            if g[t][i] == null:continue
            if dist[i] > dist[t] + g[t][i]:
                dist[i] = min(dist[i], dist[t] + g[t][i])
                if not st[i]:  
                    q.append(i)
                    st[i] = True
    if dist[n] > null / 2: print('impossible')
    else: print(dist[n])




N = 510
null = 0x3f3f3f3f
dist, st = [null] * N, [False] * N
g = [[null] * N for _ in range(N)]
n, m = map(int,input().split())
for _ in range(m):
    x, y, z = map(int,input().split())
    g[x][y] = min(g[x][y], z)
spfa()

9.spfaは負のループを決定します

彼の負のループの判断方法は実にシンプルで、n 回以上更新されたエッジがあれば負のループがあることを意味するので、元のコードを少し変更するだけで済みます。ポイント 1 が負のループに到達できない可能性があるため、すべてのポイントをキューに追加する必要があることを示します。

具体的なコードは以下の通り

##我还是不喜欢用链表存,我感觉有点麻烦
from collections import deque


def spfa():
    dist[1] = 0
    q = deque([x for x in range(1,n+1)])  # 是因为可能从1号点走不到负环。所以需要以所有点都走一下试试
    while q:
        t = q.popleft()    # 弹出开头的元素
        st[t] = False      # 他就不在队列中了
        for i in range(1, n + 1):   # 这一步其实是要找到和t相连的结点,但是我没什么好的办法,只能遍历所有的
            if g[t][i] == null:continue
            if dist[i] > dist[t] + g[t][i]:
                dist[i] = min(dist[i], dist[t] + g[t][i])
                cnt[i] = cnt[t] + 1
                if cnt[i] >= n : return 'Yes'
                if not st[i]:  
                    q.append(i)
                    st[i] = True
    return 'No'



if __name__ == '__main__':
    N = 510
    null = 0x3f3f3f3f
    dist, st = [null] * N, [False] * N
    cnt = [0] * N
    g = [[null] * N for _ in range(N)]
    n, m = map(int,input().split())
    for _ in range(m):
        x, y, z = map(int,input().split())
        g[x][y] = min(g[x][y], z)
    spfa()

10.フロルド

この n^3 アプローチは非常に暴力的です (笑)、これについては何も言うことはありません。ただ 3 つだけで完了です。
ここに画像の説明を挿入します

具体的なコードは以下の通り

N, null= 210, 0x3f3f3f3f

def flord():
    for k in range(n + 1):
        for i in range(n + 1):
            for j in range(n + 1):
                g[i][j] = min(g[i][j], g[i][k] + g[k][j])





if __name__ == '__main__':
    n, m, k = map(int,input().split())  # k次查询
    g = [[null] * N for _ in range(N)]
    for i in range(n + 1):      # flord是在g上更新,所以要做这一步
        g[i][i] = 0
    for _ in range(m):
        x, y, z = map(int,input().split())
        g[x][y] = min(g[x][y], z)
    flord()
    for _ in range(k):
        x, y = map(int, input().split())
        if g[x][y] > null / 2: print('impossible')
        else: print(g[x][y])

おすすめ

転載: blog.csdn.net/abc1234564546/article/details/130795439