題目跳轉
思路:
題目問最多可以獲得的額外傷害,其實就是詢問在這些技能中,如何怎樣選取一個最優的發動技能順序使得攻擊加成最大。我們可以把每一個技能看作成一個圖的頂點,把每一個攻擊加成看作圖的邊,權制為\(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;
}