1. 程式人生 > >帶權二分圖最大匹配 P1500

帶權二分圖最大匹配 P1500

author ges double its 一個 rip const 方式 empty

帶權二分圖最大匹配 P1500

普通的二分圖最大匹配的權值都是1,但是現在我們要解決帶權的。

解決方法有兩個:一個是匈牙利算法但是不會,另一個是最大費用最大流。

建圖方式是這個樣子:

  • 弄出源點和匯點。源點向男的連邊,女的想匯點連邊,流量為1,費用為0。
  • 滿足條件的男的向女生連邊,流量為1,費用為權值。

然後用EK算法,套上那個求最長路的SPFA就可以求最大費用最大流了。


但是這題建圖有點坑:

  1. 名字都要統一大小寫。
  2. 邊只能從男的連向女的。這題歧視homosexual
  3. 有描述到的邊的權值就是那個權值,沒有描述到的邊權值是1。
  4. 重邊的權值看最後一條邊。說好沒重邊的呢
  5. 兩個人要連線,需要中間沒有人,就是一個計算幾何的問題。我這樣寫好像不對啊
  6. 有個人的名字叫做"Enda"。。。所以字符串要整個匹配。

代碼:

/*************************************************************************
 @Author: Garen
 @Created Time : Mon 28 Jan 2019 05:43:04 PM CST
 @File Name: P1500.cpp
 @Description:
 ************************************************************************/
#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
using std::string;
const int maxn = 75;
const int INF = 0x3f3f3f3f;
// 鄰接表
struct Edges {
    int next, to, weight, cost;
} e[1000005];
int head[maxn], tot = 1;
// 建圖
int G[maxn][maxn];
std::pair<double,double> a[maxn];
std::map<string,int> mmp;
// 網絡流
int last[maxn], pre[maxn];
int flow[maxn];
bool vis[maxn];
int dist[maxn];
int s, t;
int maxflow, maxcost;
int n;
double k;
void link(int u, int v, int w, int c) {
    e[++tot] = (Edges){head[u], v, w, c};
    head[u] = tot;
}
void add_edge(int u, int v, int c) {
    link(u, v, 1, c); link(v, u, 0, -c);
}
string lower(string str) {
    int len = str.length();
    for(int i = 0; i < len; i++) {
        if(str[i] >= ‘A‘ && str[i] <= ‘Z‘) str[i] += 32;
    }
    return str;
}
double dis(int u, int v) {
    return sqrt((a[u].first - a[v].first) * (a[u].first - a[v].first) + (a[u].second - a[v].second) * (a[u].second - a[v].second));
}
bool check(int u, int v) {
    if(dis(u, v) - k > 1e-5) return false;
    for(int i = 1; i <= 2 * n; i++) {
        if(i == u || i == v) continue;
        /*
        if(a[u].first < a[i].first && a[i].first < a[v].first)
            if(a[u].second < a[i].second && a[i].second < a[v].second) {
                double temp1 = (a[v].second - a[u].second) * (a[i].first - a[u].first);
                double temp2 = (a[v].first - a[u].first) * (a[i].second - a[u].second);
                if(fabs(temp1 - temp2) < 1e-5) return false;
            }
        */
        if(fabs(dis(u, i) + dis(i, v) - dis(u, v)) < 1e-5) return false;
    }
    return true;
}
bool spfa() {
    for(int i = 1; i <= t; i++) {
        flow[i] = last[i] = pre[i] = vis[i] = 0;
        dist[i] = -INF;
    }
    std::queue<int> q;
    dist[s] = 0; flow[s] = INF;
    q.push(s); vis[s] = true;
    while(!q.empty()) {
        int u = q.front(); q.pop(); vis[u] = false;
        for(int i = head[u]; i; i = e[i].next) {
            int v = e[i].to;
            if(e[i].weight > 0 && dist[u] + e[i].cost > dist[v]) {
                dist[v] = dist[u] + e[i].cost;
                last[v] = i;
                pre[v] = u;
                flow[v] = std::min(flow[u], e[i].weight);
                if(!vis[v]) {
                    q.push(v); vis[v] = true;
                }
            }
        }
    }
    return dist[t] != -INF;
}
void update() {
    maxflow += flow[t];
    maxcost += dist[t];
    for(int i = t; i != s; i = pre[i]) {
        e[last[i]].weight -= flow[t];
        e[last[i] ^ 1].weight += flow[t];
    }
}
void EK() {
    while(spfa()) update();
}
int main() {
    cin >> k;
    cin >> n;
    for(int i = 1; i <= n; i++)
        for(int j = n + 1; j <= n + n; j++)
            G[i][j] = 1;
    for(int i = 1; i <= 2 * n; i++) {
        string name;
        cin >> a[i].first >> a[i].second >> name;
        name = lower(name);
        mmp[name] = i;
    }
    string one, two; int temp;
    while(233) {
        cin >> one;
        if(one == "End") break;
        cin >> two >> temp;
        one = lower(one); two = lower(two);
        G[mmp[one]][mmp[two]] = G[mmp[two]][mmp[one]] = temp;
    }
    for(int i = 1; i <= n; i++)// fixed
        for(int j = n + 1; j <= 2 * n; j++)// fixed
            if(check(i, j)) {
                add_edge(i, j, G[i][j]);
                //cout << "shit" << endl;
            }
    s = 2 * n + 1; t = s + 1;
    for(int i = 1; i <= n; i++) add_edge(s, i, 0), add_edge(n + i, t, 0);// fixed
    EK();
    cout << maxcost << endl;
    return 0;
}

帶權二分圖最大匹配 P1500