[bzoj2539][网络流-费用流]丘比特的烦恼

35 篇文章 0 订阅

Description

随着社会的不断发展,人与人之间的感情越来越功利化。最近,爱神丘比特发现,爱情也已不再是完全纯洁的了。
这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭。于是丘比特千里迢迢远赴中国,
找到了掌管东方人爱情的神——月下老人,向他求教。月下老人告诉丘比特,纯洁的爱情并不是不存在,而是他没
有找到。在东方,人们讲究的是缘分。月下老人只要做一男一女两个泥人,在他们之间连上一条红线,那么它们所
代表的人就会相爱——无论他们身处何地。而丘比特的爱情之箭只能射中两个距离相当近的人,选择的范围自然就
小了很多,不能找到真正的有缘人。丘比特听了月下老人的解释,茅塞顿开,回去之后用了人间的最新科技改造了
自己的弓箭,使得丘比特之箭的射程大大增加。这样,射中有缘人的机会也增加了不少。情人节(Valentine’s day
)的午夜零时,丘比特开始了自己的工作。他选择了一组数目相等的男女,感应到他们互相之间的缘分大小,并依
此射出了神箭,使他们产生爱意。他希望能选择最好的方法,使被他选择的每一个人被射中一次,且每一对被射中
的人之间的缘分的和最大。当然,无论丘比特怎么改造自己的弓箭,总还是存在缺陷的。首先,弓箭的射程尽管增
大了,但毕竟还是有限的,不能像月下老人那样,做到“千里姻缘一线牵”。其次,无论怎么改造,箭的轨迹终归
只能是一条直线,也就是说,如果两个人之间的连线段上有别人,那么莫不可向他们射出丘比特之箭,否则,按月
下老人的话,就是“乱点鸳鸯谱”了。作为一个凡人,你的任务是运用先进的计算机为丘比特找到最佳的方案。

Input

第一行为正整数k,表示丘比特之箭的射程 第二行为正整数n(n<30) 随后有2n行,表示丘比特选中的人的信息 其中前n行为男子
后n行为女子。 每个人的信息由两部分组成:他的姓名和他的位置。 姓名是长度小于20且仅包含字母的字符串,忽略大小写的区别
位置是由一对整数表示的坐标, 它们之间用空格分隔。格式为Name x y。 输入剩下的部分描述了这些人的缘分。 每一行的格式为Name1
Name2 p。 Name1和Name2为有缘人的姓名,p是他们之间的缘分值(p为小于等于255的正整数)。 以一个End作为文件结束标志。
每两个人之间的缘分至多只被描述一次。 如果没有被描述,则说明他们缘分值为1。

Output

仅一个正整数,表示每一对被射中的人之间的缘分的总和。这个和应当是最大的。

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

题解

先放一段discuss里提到的坑点
——————————————————分割线——————————————————————
①名字忽略大小写的区别~读进来以后全都转化成大写或者小写
②没有提到的那些人之间的边都是1!
③不合法的边都要赋值成-inf,不能是0
④判断连线上有没有别的点的时候可以搞个计算几何板子上去然后用叉积,如果不用叉积的话注意判断分母为0的情况
⑤读边的时候男的和女的读进来的顺序是不确定的,要自己判断交换一下= =并不知道有没有男的和女的名字是一样的
⑥数据中会出现重边2333!然而不要取Max,直接无脑覆盖就可以了2333
⑦判断退出的时候不能只判断读入的字符串前三个字符是“End”就退出因为有一组数据里面有一个叫做Enda的人蛤蛤蛤
——————————————————分割线——————————————————————
我写了个二分图转费用流的模型。。
字典树记录人名,用叉积判一下两点的连线是否过第三点
男生拆点i,i+n*2
女生拆点j+n,j+n*3
如果男生i能射向女生j
那么i->j+n连边,容量1,费用给出
j+n*2->i+n*3连边,容量1,费用给出
其余没有给出关系的,且能相互射到的,一样连边,费用为1
跑一遍最大费用最大流,结果除2即可
据说还可以用KM做??不会2333

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct pt{int x,y,p;}U[65],V[65];
struct node
{
    int x,y,c,d,next,other;
}a[110000];int len,last[210];
void ins(int x,int y,int c,int d)
{
    int k1,k2;
    k1=++len;
    a[len].x=x;a[len].y=y;a[len].c=c;a[len].d=d;
    a[len].next=last[x];last[x]=len;
    k2=++len;
    a[len].x=y;a[len].y=x;a[len].c=0;a[len].d=-d;
    a[len].next=last[y];last[y]=len;
    a[k1].other=k2;a[k2].other=k1;
}
int d[210],pre[210],tmp[210];
int list[11000],head,tail,st,ed,n;
bool v[210];
bool spfa()
{
    memset(d,-63,sizeof(d));d[st]=0;
    memset(v,false,sizeof(v));v[st]=true;
    list[1]=st;head=1;tail=2;
    while(head!=tail)
    {
        int x=list[head];
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(d[y]<d[x]+a[k].d && a[k].c>0)
            {
                d[y]=d[x]+a[k].d;
                pre[y]=x;tmp[y]=k;
                if(v[y]==false)
                {
                    v[y]=true;
                    list[tail++]=y;
                    if(tail==n*4+3)tail=1;
                }
            }
        }
        v[x]=false;
        head++;if(head==n*4+3)head=1;
    }
    if(d[ed]>-1044266559)return true;
    return false;
}
int mcf()
{
    int ret=0,minn;
    while(spfa())
    {
        int x=ed;minn=999999999;
        while(x!=st)
        {
            minn=min(minn,a[tmp[x]].c);
            x=pre[x];
        }
        ret+=d[ed]*minn;
        x=ed;
        while(x!=st)
        {
            a[tmp[x]].c-=minn;a[a[tmp[x]].other].c+=minn;
            x=pre[x];
        }
    }
    return ret;
}
double sqr(double x){return x*x;}
double dis(pt n1,pt n2){return sqrt(sqr(n1.x-n2.x)+sqr(n1.y-n2.y));}
char ch[25];
void change(int len){for(int i=1;i<=len;i++)if(ch[i]>='A' && ch[i]<='Z')ch[i]=ch[i]-'A'+'a';}
struct Tire
{
    int son[30],s;
    Tire(){memset(son,-1,sizeof(son));}
}tr[210000];int trlen,cnt,root;
void add()
{
    int ln=strlen(ch+1),p=root;
    for(int i=1;i<=ln;i++)
    {
        int y=ch[i]-'a'+1;
        if(tr[p].son[y]==-1)tr[p].son[y]=++trlen;
        p=tr[p].son[y];
    }
    tr[p].s=++cnt;
}
int fd()
{
    int ln=strlen(ch+1),p=root;
    for(int i=1;i<=ln;i++)
        p=tr[p].son[ch[i]-'a'+1];
    return tr[p].s;
}
int multi(pt p1,pt p2,pt p0)
{
    int x1,y1,x2,y2;
    x1=p1.x-p0.x;
    x2=p2.x-p0.x;
    y1=p1.y-p0.y;
    y2=p2.y-p0.y;
    return x1*y2-x2*y1;
}
bool chk(pt n1,pt n2)
{
    for(int i=1;i<=n;i++)if(U[i].x!=n1.x || U[i].y!=n1.y)
    {
        if(multi(n1,n2,U[i])==0)
        {
            if(n1.x>n2.x && (U[i].x<n2.x || U[i].x>n1.x))continue;
            if(n1.x<n2.x && (U[i].x<n1.x || U[i].y>n2.x))continue;
            if(n1.y>n2.y && (U[i].y>n1.y || U[i].y<n2.y))continue;
            if(n1.y<n2.y && (U[i].y>n2.y || U[i].y<n1.y))continue;
            return false;
        }
    }
    for(int i=n+1;i<=2*n;i++)if(V[i].x!=n2.x || V[i].y!=n2.y)
    {
        if(multi(n1,n2,V[i])==0)
        {
            if(n1.x>n2.x && (V[i].x<n2.x || V[i].x>n1.x))continue;
            if(n1.x<n2.x && (V[i].x<n1.x || V[i].y>n2.x))continue;
            if(n1.y>n2.y && (V[i].y>n1.y || V[i].y<n2.y))continue;
            if(n1.y<n2.y && (V[i].y>n2.y || V[i].y<n1.y))continue;
            return false;
        }
    }
    return true;
}
double T;
int DIS[110][110];
bool vis[110][110];
int main()
{
    scanf("%lf",&T);
    scanf("%d",&n);
    st=n*4+1;ed=n*4+2;
    root=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&U[cnt+1].x,&U[cnt+1].y);U[cnt+1].p=cnt+1;
        scanf("%s",ch+1);
        change(strlen(ch+1));
        add();
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&V[cnt+1].x,&V[cnt+1].y);V[cnt+1].p=cnt+1;
        scanf("%s",ch+1);
        change(strlen(ch+1));
        add();
    }
    memset(vis,false,sizeof(vis));
    len=0;memset(last,0,sizeof(last));
    while(scanf("%s",ch+1)!=EOF)
    {
        int tmpx,tmpy,cost;
        if(ch[1]=='E' && ch[2]=='n' && ch[3]=='d' && strlen(ch+1)==3)break;
        change(strlen(ch+1));
        tmpx=fd();
        scanf("%s",ch+1);
        change(strlen(ch+1));
        tmpy=fd();
        if(tmpx>tmpy)swap(tmpx,tmpy);//男在前 女在后 
        scanf("%d",&cost);
        vis[tmpx][tmpy]=vis[tmpy][tmpx]=true;
        if(dis(U[tmpx],V[tmpy])<=T && chk(U[tmpx],V[tmpy]))DIS[tmpx][tmpy]=cost;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            if(vis[i][j+n]==false)
            {
                vis[i][j+n]=vis[j+n][i]=true;
                if(dis(U[i],V[j+n])<=T && chk(U[i],V[j+n]))ins(i,j+n,1,1),ins(j+n*2,i+n*3,1,1);
            }
            else
            {
                if(dis(U[i],V[j+n])<=T && chk(U[i],V[j+n]))ins(i,j+n,1,DIS[i][j+n]),ins(j+n*2,i+n*3,1,DIS[i][j+n]);
            }
        }
    for(int i=1;i<=n;i++)ins(st,i,1,0),ins(st,i+n*2,1,0),ins(i+n,ed,1,0),ins(i+n*3,ed,1,0);
//  for(int i=1;i<=len;i+=2)printf("%d %d %d %d\n",a[i].x,a[i].y,a[i].c,a[i].d);
    printf("%d\n",mcf()/2);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值