P1500 丘比特的烦恼(KM&MCMF)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1500 丘比特的烦恼(KM&MCMF)相关的知识,希望对你有一定的参考价值。
P1500 丘比特的烦恼(KM)
建图跑KM即可。判断两点是否被挡住,可以枚举,然后判断两距离之和是否等于该距离。
坑点:
- 名字大小写不敏感
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mst(a,b) memset(a,b,sizeof a)
const int N=505;
const ll inf=1e18;
#define rep(i,a,b) for(int i=a;i<=b;++i)
//KM O(n^3)
bitset<N>vis;//vis[i]右部结点i是否已经匹配
//pre[y]右部结点y的上一个右部结点,slk[y]右部结点y的可松弛量
//mh[y]右部结点y的匹配结点,ex[i],ey[i]表示左部和右部结点的顶标.
int pre[N],mh[N];
ll slk[N],ex[N],ey[N],w[N][N];
int n,m;
void match(int u)//x当前访问的左部结点,y和yy用于寻找增广路径.
int x,y=0,yy=0;
ll delta;
for(int i=1;i<=n;i++) slk[i]=inf,pre[i]=0;mh[y]=u;
do //寻找增广路径.
x=mh[y],delta=inf,vis[y]=1;
for(int i=1;i<=n;i++)
if(vis[i]) continue;
if(slk[i]>ex[x]+ey[i]-w[x][i])
slk[i]=ex[x]+ey[i]-w[x][i];pre[i]=y;
if(slk[i]<delta) delta=slk[i],yy=i;
for(int i=0;i<=n;i++)
if(vis[i]) ex[mh[i]]-=delta,ey[i]+=delta;
else slk[i]-=delta;
y=yy;
while(mh[y]);
while(y) mh[y]=mh[pre[y]],y=pre[y];
ll KM()
for(int i=1;i<=n;i++) mh[i]=ex[i]=ey[i]=0;
for(int i=1;i<=n;i++) vis.reset(),match(i);
ll ans=0;for(int i=1;i<=n;i++) if(mh[i]) ans+=w[mh[i]][i];
return ans;
struct node
int x,y;
a[N];
double dis(node &a,node &b)
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
map<string,int>mp;
const double eps = 1e-8;
int main()
ll k;scanf("%lld%d",&k,&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) w[i][j]=1;
rep(i,1,(n<<1))
scanf("%d%d",&a[i].x,&a[i].y);
string s;cin>>s;
for(auto &ch:s) ch=toupper(ch);
mp[s]=i;
string su,sv;
int d;
while(cin>>su)
if(su=="End") break;
cin>>sv>>d;
for(auto &ch:su) ch=toupper(ch);
for(auto &ch:sv) ch=toupper(ch);
int u=mp[su],v=mp[sv];
if(u>v) swap(u,v);
//printf("(%d,%d,%d)\\n",u,v-n,d);
w[u][v-n]=d;
rep(i,1,n)
rep(j,1,n)
double d0;
if( ( d0=dis(a[i],a[j+n]) )<=k)
int ok=1;
rep(kk,1,n)
if(kk==i) continue;
double d1=dis(a[i],a[kk]);
double d2=dis(a[kk],a[j+n]);
if(fabs(d0-d1-d2)<eps)
ok=0;break;
if(ok) rep(kk,1,n)
if(kk==j) continue;
double d1=dis(a[i],a[n+kk]);
double d2=dis(a[n+kk],a[j+n]);
if(fabs(d0-d1-d2)<eps)
ok=0;break;
if(!ok) w[i][j]=-inf;
else w[i][j]=-inf;
printf("%lld\\n",KM());
//for(int i=1;i<=n;i++) printf("%d ",mh[i]);
return 0;
也可以 建负边 跑最大费用最大流。
源点连男生,汇点连女生。
所有边容量为1,然后可以匹配的男女生之间连费用为 − c o s t ( u , v ) -cost(u,v) −cost(u,v)的边,跑MCMF即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mst(a,b) memset(a,b,sizeof a)
const int N=505,M=2e5+5;
const ll inf=1e18;
#define rep(i,a,b) for(int i=a;i<=b;++i)
int cnt=1,h[N],flow[N],dis[N],vis[N],n,m,st,ed;
ll mc,mf;
int id(int x,int y)
return (x-1)*n+y;
queue<int>q;
struct edge
int to,nt,f,w;//f:flow ,w:cost
e[M];
struct Pre
int i,u;
pre[N];//记录前驱结点和边的信息,便于更新残余网络,建立反边.(反悔和贪心的思想)
void add(int u,int v,int f,int w)
e[++cnt]=v,h[u],f,w,h[u]=cnt;
e[++cnt]=u,h[v],0,-w,h[v]=cnt;
bool spfa()// 跑spfa
mst(dis,0x3f),mst(flow,0x3f),mst(vis,0); //初始化.
q.push(st),vis[st]=1,dis[st]=0,pre[ed].u=-1;//预处理
while(!q.empty())
int u=q.front();q.pop();vis[u]=0;
for(int i=h[u];i;i=e[i].nt)
int v=e[i].to,f=e[i].f,w=e[i].w;
if(f>0&&dis[v]>dis[u]+w)
dis[v]=dis[u]+w;
pre[v].u=u,pre[v].i=i;
flow[v]=min(flow[u],f);
if(!vis[v]) vis[v]=1,q.push(v);
return pre[ed].u!=-1;
void MCMF() //MIncost Maxflow
while(spfa())
int u=ed,x=flow[ed];
mf+=x;
mc+=x*dis[u];
while(u!=st)
e[pre[u].i].f-=x;
e[pre[u].i^1].f+=x;
u=pre[u].u;
struct node
int x,y;
a[N];
double fun_dis(node &a,node &b)
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
map<string,int>mp;
ll w[N][N];
const double eps = 1e-8;
int main()
ll k;scanf("%lld%d",&k,&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) w[i][j]=1;
rep(i,1,(n<<1))
scanf("%d%d",&a[i].x,&a[i].y);
string s;cin>>s;
for(auto &ch:s) ch=toupper(ch);
mp[s]=i;
string su,sv;
int d;
while(cin>>su)
if(su=="End") break;
cin>>sv>>d;
for(auto &ch:su) ch=toupper(ch);
for(auto &ch:sv) ch=toupper(ch);
int u=mp[su],v=mp[sv];
if(u>v) swap(u,v);
//printf("(%d,%d,%d)\\n",u,v-n,d);
w[u][v-n]=d;
rep(i,1,n)
rep(j,1,n)
double d0;
if( ( d0=fun_dis(a[i],a[j+n]) )<=k)
int ok=1;
rep(kk,1,n)
if(kk==i) continue;
double d1=fun_dis(a[i],a[kk]);
double d2=fun_dis(a[kk],a[j+n]);
if(fabs(d0-d1-d2)<eps)
ok=0;break;
if(ok) rep(kk,1,n)
if(kk==j) continue;
double d1=fun_dis(a[i],a[n+kk]);
double d2=fun_dis(a[n+kk],a[j+n]);
if(fabs(d0-d1-d2)<eps)
ok=0;break;
if(!ok) w[i][j]=-inf;
else w[i][j]=-inf;
if(w[i][j] == -inf) continue;
bzoj2539: [Ctsc2000]丘比特的烦恼