魏迟燕的自走棋
F题 魏迟燕的自走棋 题解(贪心+匈牙利)
题意
有 \(n\) 个人,\(m\) 件装备,每个人只能装备一件装备,每件装备只能分配给一个人。
其中,第 \(i\) 件装备可以给 \(k_i\) 个人中的一个,分别为 \(p_1,...,p_{k_i}\) 获得的战力提升为 \(w_i\) ,总战力提升即为所有士兵战力提升之和。
魏迟燕想知道他能获得的最大总战力提升为多少?
数据范围:\(1≤n,m≤1e5,1≤k_i≤2,1≤p_i≤n,1≤wi≤1e9\)
思路
因为价值只与装备有关,所以可以先按价值排序(从大到小),然后用匈牙利算法找到最大匹配量就相当于找到了最大权匹配,算法中要求每用一个新的装备进行匹配时都要清空vis数组(主要用于判断装备是否可以进行重新匹配),但是由于本题装备最多只有两个选择,所以可以确定如果为了匹配新装备而改变已匹配装备的匹配对象而失败时进行重新匹配的装备就失去了重新匹配的可能,所以本题就没必要去清空vis数组,只用清空那些匹配成功的装备即可,而未清空数组还能起到剪枝的作用。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+1;
struct node {
long long k, a[3], c;
bool operator< (const node n1) {
return c > n1.c;
}
}p[maxn];
int match[maxn];
bool vis[maxn];
bool dfs(int x) {
for(int i = 1; i <= p[x].k; ++ i) {
int j = p[x].a[i];
if (vis[j]) continue;
vis[j] = true;
if (!match[j] || dfs(match[j])) {
match[j] = x;
vis[j] = false;
return true;
}
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for(int i = 1; i <= m; ++ i) {
cin >> p[i].k;
for(int j = 1; j <= p[i].k; ++ j) {
cin >> p[i].a[j];
}
cin >> p[i].c;
}
sort(p+1, p+m+1);
long long res = 0;
for(int i = 1; i <= m; ++ i) {
if (dfs(i)) res += p[i].c;
}
cout << res << "\n";
return 0;
}
注意,该方法只在左子图任意点的边数都不超过两条的情况适用,否则就必须对vis进行清空。以下是有一组hack样例(结果为4,但不清空vis可能导致结果为3)可自行推理,
4 4 8
1 2
1 3
2 3
2 2
2 1
3 3
3 4
4 2