【題解】A18537.我心中珍藏的遊戲

Macw發表於2024-03-18

題目跳轉

思路:

題目問最多可以獲得的額外傷害,其實就是詢問在這些技能中,如何怎樣選取一個最優的發動技能順序使得攻擊加成最大。我們可以把每一個技能看作成一個圖的頂點,把每一個攻擊加成看作圖的邊,權制為\(Ei,j\)。由於\(Ei,j\)\(Ej,i\)相等,則可以將這個圖視為無向圖。 可以樣樣例抽象成下圖:

3
0 3 5
3 0 10
5 10 0

圖片

考慮使用貪心的思想來解決本題,每次在圖中找到權值最大的一條邊選擇即可,但圖中不能出現環。因為是無向圖,在考慮的時候可以忽略技能使用的順序。接下來就是找一個最大生成樹即可。

時間複雜度分析:本道題可以使用最小生成樹Kruskal演算法來實現,將題目的模型抽象化後可以被看作為一個最多有\(\frac{(1+n)*n}{2} - n\)條邊的無向圖(化簡後可得\(\frac{n^2 - n}{2}\))。注意一開始需要對每一條邊進行排序。本道題的時間複雜度約為\(O(2\times n^2 \log_2{n})\)

參考程式碼:

#include <iostream>
#include <algorithm>
using namespace std;

int n, cnt, ans;
int f[805];
struct edge{
    int x, y, z;
} edges[700005];

bool cmp(edge a, edge b){
    return a.z > b.z;
}

int getf(int x){
    if (f[x] == x) return x;
    return f[x] = getf(f[x]);
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i=1; i<=n; i++){
        f[i] = i;
        for (int j=1; j<=n; j++){
            int t; cin >> t;
            if (i == j || t == 0) continue;
            edges[++cnt] = (edge){i, j, t};
        }
    }
    sort(edges+1, edges+1+cnt, cmp);
    for (int i=1; i<=cnt; i++){
        int u = edges[i].x;
        int v = edges[i].y;
        int U = getf(u), V = getf(v);
        if (U != V){
            f[U] = V;
            ans += edges[i].z;
        }
    }
    cout << ans << endl;
    return 0;
}

相關文章