[BZOJ2539][CTSC2000][KM]丘比特的烦恼

[Problem Description]
  随着社会的不断发展,人与人之间的感情越来越功利化。最近,爱神丘比特发现,爱情也已不再是完全纯洁的了。这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭。于是丘比特千里迢迢远赴中国,找到了掌管东方人爱情的神——月下老人,向他求教。
  月下老人告诉丘比特,纯洁的爱情并不是不存在,而是他没有找到。在东方,人们讲究的是缘分。月下老人只要做一男一女两个泥人,在他们之间连上一条红线,那么它们所代表的人就会相爱——无论他们身处何地。而丘比特的爱情之箭只能射中两个距离相当近的人,选择的范围自然就小了很多,不能找到真正的有缘人。
  丘比特听了月下老人的解释,茅塞顿开,回去之后用了人间的最新科技改造了自己的弓箭,使得丘比特之箭的射程大大增加。这样,射中有缘人的机会也增加了不少。
  情人节(Valentine's day)的午夜零时,丘比特开始了自己的工作。他选择了一组数目相等的男女,感应到他们互相之间的缘分大小,并依此射出了神箭,使他们产生爱意。他希望能选择最好的方法,使被他选择的每一个人被射中一次,且每一对被射中的人之间的缘分的和最大。
  当然,无论丘比特怎么改造自己的弓箭,总还是存在缺陷的。首先,弓箭的射程尽管增大了,但毕竟还是有限的,不能像月下老人那样,做到“千里姻缘一线牵”。其次,无论怎么改造,箭的轨迹终归只能是一条直线,也就是说,如果两个人之间的连线段上有别人,那么莫不可向他们射出丘比特之箭,否则,按月下老人的话,就是“乱点鸳鸯谱”了。
  作为一个凡人,你的任务是运用先进的计算机为丘比特找到最佳的方案。
[Algorithm]
KM
[Analysis]
接近裸题……
[Pay Attention]
1.读入的名字是不区分大小写的,所以做的时候要先将字符串全部转换为大写或者小写
2.当判断两个人之间无法连线时, 应该将它们之间的边的权值设为-INF而不是0!
[Code]
/**************************************************************
    Problem: 2539
    User: gaotianyu1350
    Language: C++
    Result: Accepted
    Time:32 ms
    Memory:1288 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <map>
using namespace std;
 
const int MAXN = 40;
 
bool luse[MAXN], ruse[MAXN];
int result[MAXN], l[MAXN], r[MAXN], slack[MAXN];
int gap[MAXN][MAXN];
int n, dis, x[MAXN * 2], y[MAXN * 2];
map<string, int> toname;
 
void strrupr(char * s)
{
    for (int i = 0; i < strlen(s); i++)
        if ('a' <= s[i] && s[i] <= 'z')
        {
            s[i] = 'A' + s[i] - 'a';
        } 
}
 
bool find(int x)
{
    luse[x] = true;
    for (int i = 1; i <= n; i++)
    if (!ruse[i])
    {
        int temp = l[x] + r[i] - gap[x][i];
        if (!temp)
        {
            ruse[i] = true;
            if (!result[i] || find(result[i]))
            {
                result[i] = x;
                return true;
            }
        }
        else
            slack[i] = min(slack[i], temp);
    }
    return false;
}
 
inline int km()
{
    memset(result, 0, sizeof(result));
    memset(l, 0, sizeof(l));
    memset(r, 0, sizeof(r));
    for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
        l[i] = max(l[i], gap[i][j]);
    for (int i = 1; i <= n; i++)    
    {
        memset(slack, 0x7f, sizeof(slack));
        while (1)
        {
            memset(luse, 0, sizeof(luse));
            memset(ruse, 0, sizeof(ruse));
            if (find(i)) break;
            int minn = 1000000000;
            for (int i = 1; i <= n; i++)
                if (!ruse[i])
                    minn = min(minn, slack[i]);
            for (int i = 1; i <= n; i++)
            {
                if (luse[i]) l[i] -= minn;
                if (ruse[i]) r[i] += minn;
                else slack[i] -= minn;
            }
        }
    }
    int sum = 0;
    for (int i = 1; i <= n; i++)
        sum += l[i] + r[i];
    return sum;
}
 
inline void init()
{
    int xmin, xmax, ymin, ymax;
    for (int i = 1; i <= n; i++)
    for (int j = n + 1; j <= n * 2; j++)
    {
        if ((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]) > dis * dis)
        {
            gap[i][j - n] = -1000000000;
            continue;
        }
        for (int k = 1; k <= n * 2; k++)
        if (k != i && k != j)
        {
            xmin = min(x[i], x[j]);
            ymin = min(y[i], y[j]);
            xmax = max(x[i], x[j]);
            ymax = max(y[i], y[j]);
            if (xmin <= x[k] && x[k] <= xmax && ymin <= y[k] && y[k] <= ymax &&
                (y[k] - y[i]) * (x[j] - x[i]) == (y[j] - y[i]) * (x[k] - x[i]))
            {
                gap[i][j - n] = -1000000000;
                break;
            }
        }
    }
}
 
int main()
{
    //freopen("input.txt", "r", stdin);
    scanf("%d%d", &dis, &n);
    for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
        gap[i][j] = 1;
    toname.clear();
    string strName;
    char lpszName[100];
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d%s", &x[i], &y[i], lpszName);
        strrupr(lpszName);
        strName = lpszName;
        toname[strName] = i;
    }
    for (int i = n + 1; i <= n * 2; i++)
    {
        scanf("%d%d%s", &x[i], &y[i], lpszName);
        strrupr(lpszName);  
        strName = lpszName;
        toname[strName] = i;
    }
    while (1)
    {
        scanf("%s", lpszName);
        strrupr(lpszName);
        strName = lpszName;
        if (strName == "END") break;
        int theX = toname[strName];
        scanf("%s", lpszName);
        strrupr(lpszName);
        strName = lpszName;
        int theY = toname[strName];
        int temp;
        scanf("%d", &temp);
        if (theX > theY)
        {
            int t = theX; theX = theY; theY = t;
        }
        gap[theX][theY - n] = temp;
    }
    init();
    printf("%d\n", km());
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值