【尤拉路】Codeforces Round #508 (Div. 2) E. Maximum Matching
阿新 • • 發佈:2018-12-24
Step1 Problem:
給你 n 個塊,每個塊左右兩邊有顏色,中間是塊的權值,如果不同的塊的邊顏色一樣,那麼它們可以合併成新的一塊。
例:兩個塊分別是 c1 v1 c2, c2 v2 c1,那麼這兩個塊可以變成 c1 v1+v2 c1,或者 c2 v1+v2 c2。
你可以選擇 n 塊的任意塊,求合成後的權值最大值。
資料範圍:
1<=n<=100, 1 <= c <= 4, 1 <= v <= 100000.
Step2 Ideas:
尤拉路徑:如果圖 G 中的一個路徑包括每個邊恰好一次,則該路徑稱為尤拉路徑。
歐拉回路:如果一個迴路是尤拉路徑,則稱為歐拉回路。
一個圖奇點個數一定是偶數個
如果一個無向圖是連通的,且最多隻有兩個奇點,則一定存在尤拉道路。
因為本題最多隻有四個點,當連通塊個數是 1 的時候 同時 四個點都是奇點,當前不是尤拉路。
否則都是尤拉路,如果是尤拉路代表能一筆走完,就代表可以把該連通塊所有塊都連一起,權值就是所有塊的和。
核心點:如果不是尤拉路的話,代表不能一筆走完,我們就得去除某條邊,對於當前圖找最大權值的尤拉路。
Step3 Code:
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
struct node
{
int to, w, rev, flag;//目的,權值,反向邊,標記該邊是否存在
};
int vis[10], d[10], c;
int bs[10];
vector<node> Map[5];
void dfs(int u)
{
vis[u] = c;
for(int i = 0; i < Map[u].size(); i++) {
int to = Map[u][i].to, flag = Map[u][i].flag;
if(flag == 1) continue ;
if(!vis[to]) dfs(to);
}
}
int solve()
{
c = 0;
int cnt = 0, odd = 0;
int ans = 0;
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= 4; i++) {
if(!vis[i]) {
c++;
dfs(i), cnt++;
int sum = 0;
for(int j = 1; j <= 4; j++) {
if(vis[j] == c) {
if(d[j]&1) odd++;
sum += bs[j];
}
}
ans = max(ans, sum);
}
}
if(cnt == 1 && odd == 4) return 0;//不是尤拉路
else return ans;//該圖存在尤拉路,返回權值最大的尤拉路
}
int main()
{
int n, u, v, w;
scanf("%d", &n);
memset(d, 0, sizeof(d));//記錄每個點的度
memset(bs, 0, sizeof(bs));//記錄到 v 的權值和
for(int i = 1; i <= n; i++) {
scanf("%d %d %d", &u, &w, &v);
d[u]++, d[v]++;
bs[v] += w;
Map[u].push_back((node){v, w, Map[v].size(), 0});
Map[v].push_back((node){u, w, Map[u].size()-1, 0});
}
int ans = 0;
ans = solve();
if(ans) {
printf("%d\n", ans);
return 0;
}
//刪除邊,找權值最大的尤拉路
for(int i = 1; i <= 4; i++) {
for(int j = 0; j < Map[i].size(); j++) {
int to = Map[i][j].to, w = Map[i][j].w, &flag = Map[i][j].flag;
if(to == i) continue;
flag = 1;//刪邊
Map[to][Map[i][j].rev].flag = 1;
d[i]--, d[to]--;//減度
ans = max(ans, solve()-w);//更新最大值
Map[to][Map[i][j].rev].flag = 0;//恢復邊
flag = 0;
d[i]++, d[to]++;
}
}
printf("%d\n", ans);
return 0;
}