1. 程式人生 > >bzoj2539 丘比特的煩惱、黑書P333 (最優二分圖匹配)

bzoj2539 丘比特的煩惱、黑書P333 (最優二分圖匹配)

def 等於 ostream while 感情 deb valentine 區分 匹配

丘比特的煩惱

題目描述 Description

  隨著社會的不斷發展,人與人之間的感情越來越功利化。最近,愛神丘比特發現,愛情也已不再是完全純潔的了。這使得丘比特很是苦惱,他越來越難找到合適的男女,並向他們射去丘比特之箭。於是丘比特千裏迢迢遠赴中國,找到了掌管東方人愛情的神——月下老人,向他求教。
  月下老人告訴丘比特,純潔的愛情並不是不存在,而是他沒有找到。在東方,人們講究的是緣分。月下老人只要做一男一女兩個泥人,在他們之間連上一條紅線,那麽它們所代表的人就會相愛——無論他們身處何地。而丘比特的愛情之箭只能射中兩個距離相當近的人,選擇的範圍自然就小了很多,不能找到真正的有緣人。
  丘比特聽了月下老人的解釋,茅塞頓開,回去之後用了人間的最新科技改造了自己的弓箭,使得丘比特之箭的射程大大增加。這樣,射中有緣人的機會也增加了不少。
  情人節(Valentine‘s day)的午夜零時,丘比特開始了自己的工作。他選擇了一組數目相等的男女,感應到他們互相之間的緣分大小,並依此射出了神箭,使他們產生愛意。他希望能選擇最好的方法,使被他選擇的每一個人被射中一次,且每一對被射中的人之間的緣分的和最大。
  當然,無論丘比特怎麽改造自己的弓箭,總還是存在缺陷的。首先,弓箭的射程盡管增大了,但畢竟還是有限的,不能像月下老人那樣,做到“千裏姻緣一線牽”。其次,無論怎麽改造,箭的軌跡終歸只能是一條直線,也就是說,如果兩個人之間的連線段上有別人,那麽莫不可向他們射出丘比特之箭,否則,按月下老人的話,就是“亂點鴛鴦譜”了。
  作為一個凡人,你的任務是運用先進的計算機為丘比特找到最佳的方案。

輸入描述 Input Description

  輸入文件第一行為正整數k,表示丘比特之箭的射程,第二行為正整數n(n<30),隨後有2n行,表示丘比特選中的人的信息,其中前n行為男子,後n行為女子。每個人的信息由兩部分組成:他的姓名和他的位置。姓名是長度小於20且僅包含字母的字符串,忽略大小寫的區別,位置是由一對整數表示的坐標,它們之間用空格分隔。格式為x y Name。輸入文件剩下的部分描述了這些人的緣分。每一行的格式為Name1 Name2 p。Name1和Name2為有緣人的姓名,p是他們之間的緣分值(p為小於等於255的正整數)。以一個End作為文件結束標誌。每兩個人之間的緣分至多只被描述一次。如果沒有被描述,則說明他們緣分值為1。

輸出描述 Output Description

  輸出文件僅一個正整數,表示每一對被射中的人之間的緣分的總和。這個和應當是最大的。

樣例輸入 Sample Input

2
3
0 0 Adam
1 1 Jack
0 2 George
1 0 Victoria
0 1 Susan
1 2 Cathy
Adam Cathy 100
Susan George 20
George Cathy 40
Jack Susan 5
Cathy Jack 30
Victoria Jack 20
Adam Victoria 15
End

樣例輸出 Sample Output

65

數據範圍及提示 Data Size & Hint

n<30

p為小於等於255的正整數

思路:

就是建圖跑二分圖最優匹配,這裏我用最小費用流求解。

幾個需要註意的地方:

  1. 點以坐標形式給出,要運用一下計算機和的知識求出兩點間距離、判斷兩點所連線段上是否有其他點;
  2. 點的特征值是一個字符串,且不區分大小寫,需要註意;
  3. 題面保證沒有重邊但數據中有重邊,且是取最後給出的邊,這裏比較坑。一是保證沒有重邊卻出現重邊debug了很久;二是想到可能存在重邊但沒想到竟不是取最好的邊debug了更久(不過使用領接矩陣的話恰好跳過了這個坑點,大概標程寫的KM吧);
  4. 數據中未給出的邊存在默認權值為一的邊,優先級低於距離限制和夾點限制。

代碼:

#include<iostream>
#include<queue>
#include<cstdio>
#include<vector>
#include<map>
#include<cmath>
#include<cstring>

using namespace std;
const int maxn = 2e4+5;
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;

struct Edge
{
    int from, to, cap, flow, cost;
};

struct MCMF
{
    int n, m, s, t;
    vector<Edge> edges;
    vector<int> G[maxn];
    int inq[maxn];//是否在隊列
    int d[maxn];//Bellman-Ford用
    int p[maxn];//上一條弧
    int a[maxn];//可改進量

    void init(int n)
    {
        this->n=n;
        for(int i=0; i<n; ++i)
            G[i].clear();
        edges.clear();
    }

    void AddEdge(int from, int to, int cap, int cost)
    {
        edges.push_back((Edge)
        {
            from, to, cap, 0, cost
        });
        edges.push_back((Edge)
        {
            to, from, 0, 0, -cost
        });
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool BellmanFord(int s, int t, int &flow, int& cost)
    {
        for(int i=0; i<n; ++i)
            d[i]=INF;
        memset(inq,0,sizeof(inq));
        memset(p,0,sizeof(p));
        d[s]=0;
        inq[s]=1;
        p[s]=0;
        a[s]=INF;

        queue<int> Q;
        Q.push(s);
        while(!Q.empty())
        {
            int u=Q.front();
            Q.pop();
            inq[u]=0;
            for(int i=0; i<G[u].size(); ++i)
            {
                Edge& e=edges[G[u][i]];
                if(e.cap > e.flow && d[e.to] > d[u] + e.cost)
                {
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to])
                    {
                        Q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }
        if(d[t]==INF)
            return false;
        flow+=a[t];
        cost+=d[t]*a[t];
        int u=t;
        while(u!=s)
        {
            edges[p[u]].flow+=a[t];
            edges[p[u]^1].flow-=a[t];
            u=edges[p[u]].from;
        }
        return true;
    }

    int MinCost(int s, int t)
    {
        int flow=0, cost=0;
        while(BellmanFord(s,t,flow,cost))
        {
        }
        return cost;
    }
} mcmf;

class Person
{
public:
    int x, y, num;
    string name;

    Person() {}

    Person(int a, int b, int c, string str):x(a), y(b), num(c), name(str)
    {
    }
};

int k, n, s, t;
int grap[100][100];
map<string, int> msi;
vector<Person> persons;

double Slope(Person& p1, Person& p2)
{
    if(p1.x==p2.x)
        return INF;
    return 1.0*(p1.y-p2.y)/(p1.x-p2.x);
}

int GetDis(Person& p1, Person& p2)
{
    int maxx=p1.x, maxy=p1.y, minx=p2.x, miny=p2.y;
    double sp=Slope(p1, p2);
    if(maxx<minx) swap(maxx, minx);
    if(maxy<miny) swap(maxy, miny);
    for(int i=0; i<persons.size(); ++i)
    {
        if(i==p1.num || i==p2.num) continue;
        Person& p3=persons[i];
        if(p3.x<=maxx && p3.x>=minx && p3.y<=maxy && p3.y>=miny && abs(sp-Slope(p1, p3))<=eps)
            return INF;
    }
    return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
}

void trans(string& str)
{
    for(int i=0; i<str.size(); ++i)
        if(str[i]>=A && str[i]<=Z)
            str[i]+=32;
}

void Init()
{
    msi.clear();
    persons.clear();
    for(int i=0; i<100; ++i)
        for(int j=0; j<100; ++j)
            grap[i][j]=1;
    cin>>k>>n;
    k*=k;
    int x, y, fate, dis;
    string name, name2;
    s=2*n;
    t=s+1;
    mcmf.init(t+1);
    for(int i=0; i<2*n; ++i)
    {
        cin>>x>>y>>name;
        trans(name);
        persons.push_back(Person(x, y, i, name));
        if(i<n)
            mcmf.AddEdge(s, i, 1, 0);
        else
            mcmf.AddEdge(i, t, 1, 0);
        msi[name]=i;
    }
    while(cin>>name)
    {
        if(name=="End")
            break;
        trans(name);
        cin>>name2>>fate;
        trans(name2);
        int p1=msi[name], p2=msi[name2];
        if(p1>p2) swap(p1, p2);
        grap[p1][p2]=fate;
    }
    for(int i=0; i<n; ++i)
        for(int j=n; j<2*n; ++j)
        {
            dis=GetDis(persons[i], persons[j]);
            if(dis<=k)
                mcmf.AddEdge(i, j, 1, -grap[i][j]);
        }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    Init();
    cout<<-mcmf.MinCost(s, t)<<endl;
    return 0;
}

bzoj2539 丘比特的煩惱、黑書P333 (最優二分圖匹配)