1. 程式人生 > >度小滿2018.9.26筆試 鏈式邊權

度小滿2018.9.26筆試 鏈式邊權

題目描述

在這裡插入圖片描述 n個點,有n-1條邊,每條邊的權值被這樣計算: 在邊左面的點稱為x,在邊右面的點稱為y。x≠y。有多少這樣的點對,那麼這條邊的權值就為多少。 提示: 第一條邊能形成一個點對(1,2) 第二條邊能形成一個點對(2,1) 所以,輸出為1 1

動態規劃

#include <pch.h>//vs2017建控制檯程式自帶的預編譯標頭檔案
#include <stdio.h>
#include <vector>
#include <iostream>
using namespace std;

int main() {
	int n;
	cin >>
n; vector<int> a(n), w(n);//w存邊的權值 for (int i = 0; i < n; i++) { scanf_s("%d", &a[i]); } for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if (a[j] != a[i]) { w[i]++; w[j]--; } } if (i != 0) { w[i] = w[i] + w[i - 1]; } printf("%d "
, w[i]); } return 0; }

重點解釋一下,內層迴圈裡面的加加和減減吧,還有w[i] = w[i] + w[i - 1];,這句一看就是很像是動態軌跡裡面的遞推式。 以一個實際例子講解吧: 輸入: 4 1 2 3 4 輸出: 3 4 3

初始時: 在這裡插入圖片描述 執行完第一次外層迴圈後: 在這裡插入圖片描述 就得到了第一條邊的權值,權值為3的含義是有這三個點對。剩下三個點的權值都為-1,假如1稱為已經記錄的點,那麼這個-1就代表當前點與已記錄點之間能形成多少點對,比如2這個點與已記錄點1之間能形成一個點對(1,2),所以2點在當前的權值為-1。(注意這裡以及之後的“權值”不是指原題中指的權值)

執行完第二次外層迴圈後:

在這裡插入圖片描述 就得到了第二條邊的權值,權值為4的含義是有這四個點對。首先這個2很好理解,從當前點2往後遍歷,發現有兩個點對(2,3)(2,4)。但是1和邊右面的點還沒有統計,但是也不需要統計,因為這個資訊肯定包括在之前記錄的點1的權值裡面即(1,2)(1,3)(1,4),但是由於(1,2)不應該包括進來,所以這時候之前在點2記錄的權值-1即(1,2)就派上了用場,這樣,(3-1)就得2即(1,3)(1,4)。

第三次外層迴圈也是一個道理。

總結一下: 1.第i次迴圈結束後,第i條邊的權值會被記錄。也稱0 - i的點們已經被記錄,因為0 - i的點們和i+1 - n-1的點們之間能形成的點對數量已經被w[i]所記錄,也就是題意的意思。

2.第i次迴圈結束後,從i+1 - n-1的點的權值也會被更新:注意i+1 - n-1的點們的權值必為負值,他的絕對值是指,當前點與已經記錄的點之間形成的點對數量。

3.除開第一次外層迴圈,每次外層迴圈i中,對w{i}的更新都是兩部分:第一是當前點i與邊右面的點之間形成的點對數,這個是用內層迴圈做的;第二是0 - i-1的點們與邊右面的點之間形成的點對數,這個資訊已經被w[i-1]儲存,但資訊不是都需要的,需要除開0 - i-1的點們與點i之間形成的點對數,而這個需要除開的數量剛好就是當前w[i]存的負值的絕對值。

動態規劃

這是第二種用法,python3程式碼。思路與上一種完全一樣,但具體細節實現有所不同。

首先重點理解下我這句話,要得到一個數x和一個數的陣列yArray之間可以形成多少個點對,只需要得到yArray的長度減去yArray中x的數量。

使用了collections.Counter(),是計數器的功能,可以得到一個鍵值key出現的次數。

import collections
 
n = eval(input())
a = list(map(int,input().split()))
 
lw = collections.Counter()
l_count = 0
rw = collections.Counter(a)
r_count = n
res = [0] * (n-1)
for i in range(n-1):
    lw[a[i]] += 1
    l_count += 1
    rw[a[i]] -= 1
    r_count -= 1
    res[i] = res[i-1] + (r_count - rw[a[i]]) - (l_count - lw[a[i]])
    #第0次迴圈不需要單獨加判斷,因為res[-1]=res[0]=0
 
print( ' '.join(map(str,res)))

和上一種程式碼一樣,在每次迴圈i中,(r_count - rw[a[i]])代表了當前點i與邊右面的點之間形成的點對數。res[i-1] - (l_count - lw[a[i]])代表0 - i-1的點們與邊右面的點之間形成的點對數,這個資訊已經被res[i-1]儲存,但需要除開一部分資訊即0 - i-1的點們與點i之間形成的點對數,需要除開的資訊剛好就是(l_count - lw[a[i]]),注意lw[a[i]])至少為1。

再解釋一下,這個需要除開的資訊,就是,邊左面的點的數量減去邊左面的點中a[i]出現的次數,即為0 - i-1的點們與點i之間形成的點對數。