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]丘比特的烦恼

丘比特的烦恼

BZOJ2539CTSC2000丘比特的烦恼

BZOJ2539: [Ctsc2000]丘比特的烦恼

bzoj2539 丘比特的烦恼黑书P333 (最优二分图匹配)

P2457 [SDOI2006]仓库管理员的烦恼 (费用流)