萌新刚学分数规划,Dinic 求最大费用最大流 RE 求助。
查看原帖
萌新刚学分数规划,Dinic 求最大费用最大流 RE 求助。
534296
aCssen楼主2023/10/24 14:58

Rt,建出的图中共有 2n+22n+2 个点,n2+2nn^2+2n 条边,所以 maxn=105,maxm=20405,除 a,ba,b 外,用到 maxnmaxn 的大小均乘 22。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=205;
const int maxm=20405;
int a[maxn][maxn],b[maxn][maxn],d[maxn<<1],now[maxn<<1],head[maxn<<1],ver[maxm],edge[maxm],nxt[maxm],tot,n,m,s,t;
double dis[maxn<<1],cost[maxn<<1],l,r,ans;
bool vis[maxn<<1];
queue<int>q;
void addedge(int u,int v,int w,double c){
	ver[++tot]=v;
	edge[tot]=w;
	cost[tot]=c;
	nxt[tot]=head[u];
	head[u]=tot;
}
void add_edge(int u,int v,int w,double c){
	addedge(u,v,w,c);
	addedge(v,u,0,-c);
}
bool spfa(){
	for(int i=1;i<=2*n+2;i++){
		dis[i]=-1e18;
		vis[i]=0;
	}
	now[s]=head[s];
	vis[s]=true;
	dis[s]=0;
	d[s]=1;
	q.push(s);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		vis[x]=false;
		for(int i=head[x];i;i=nxt[i]){
			int y=ver[i],z=edge[i];
			double w=cost[i];
			if(!z) continue;
			if(dis[x]+w>dis[y]){
				now[y]=head[y];
				dis[y]=dis[x]+w;
				d[y]=d[x]+1;
				if(!vis[y]){
					vis[y]=true;
					q.push(y);
				}
			}
		}
	}
	return dis[t]!=-1e18;
}
int dinic(int x,int flow){
	if(x==t) return flow;
	int rest=flow;
	for(int i=now[x];i&&rest;i=nxt[i]){
		now[x]=i;
		int y=ver[i],z=edge[i];
		double w=cost[i];
		if(dis[y]==dis[x]+w&&d[y]==d[x]+1&&z){
			int k=dinic(y,min(rest,z));
			if(!k) d[y]=0;
			edge[i]-=k;
			edge[i^1]+=k;
			rest-=k;
			ans+=(k*w);
		}
	}
	return flow-rest;
}
bool check(double mid){
	memset(head,0,sizeof(head));
	tot=s=1;
	t=2*n+2;
	ans=0;
	for(int i=1;i<=n;i++)
		add_edge(s,i+1,1,0);
	for(int i=1;i<=n;i++)
		add_edge(n+i+1,t,1,0);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			add_edge(i+1,j+n+1,1,a[i][j]-mid*b[i][j]);
	while(spfa()) while(dinic(s,1e9));
	return ans>0;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)	
			scanf("%d",&b[i][j]);
	r=1e6;
	for(int i=1;i<=45;i++){
		double mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%0.6lf",l);
	return 0;
}
2023/10/24 14:58
加载中...