习题:星际战争(网络流)

题目

传送门

思路

如果正向思考直接求出T

你会发现每一个激光用的时间是不同的

很难求出

但是如果我们二分T

问题就转化成为如何检测在T的时间内能否消灭所有的机器人

比较明显的网络流

我们将S连向所有的激光,权值为\(a_i*t\)

再将所有的激光连向所用能打的机器人,权值为INF

在将机器人连向E,权值为\(b_i\)

检查的是时候我们只需要检查这个图的最大流是不是\(\sum_{i=1}^n b_i\)就行了

代码

#include<iostream>
#include<vector>
#include<cstdio>
#include<climits>
#include<cstring>
using namespace std;
int n,m;
long long summ;
long long l,r,mid;
long long a[105];
long long b[105];
long long k[55][55];
struct networt_flow_isap
{
	#define MAXN 405
	long long c[MAXN][MAXN];
	int d[MAXN];
	int vd[MAXN];
	long long maxx;
	int s,t;
	vector<int> g[MAXN];
	#undef MAXN
	void init()
	{
		maxx=0;
		memset(c,0,sizeof(c));	
		memset(d,0,sizeof(d));
		memset(vd,0,sizeof(vd));
		for(int i=1;i<=n;i++)
			g[i].clear();
		n=n-m-2;
		summ=0;
		for(int i=1;i<=n;i++)
		{
			c[i+m][n+m+2]=a[i]*10000;
			summ+=a[i]*10000;
			g[i+m].push_back(n+m+2);
			g[n+m+2].push_back(i+m);
		}
		for(int i=1;i<=m;i++)
		{
			c[n+m+1][i]=b[i];	
			g[n+m+1].push_back(i);
			g[i].push_back(n+m+1);
		}
		for(int i=1;i<=m;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(k[i][j])
				{
					c[i][j+m]=(1ll<<40);
					g[i].push_back(j+m);
					g[j+m].push_back(i);
				}
			}
		}
		n=n+m+2;
	}
	long long dfs(int u,long long f)
	{
		if(u==t)
			return f;
		long long minn=0,summ=0,id=n-1;
		for(int i=0;i<g[u].size();i++)
		{
			int v=g[u][i];
			if(c[u][v]>0)
			{
				if(d[u]==d[v]+1)
				{
					minn=min(f-summ,c[u][v]);
					minn=dfs(v,minn);
					c[u][v]-=minn;
					c[v][u]+=minn;
					summ+=minn;
					if(d[s]>=n)
						return summ;
					if(summ==f)
						break;
				}
				if(d[v]<id)
					id=d[v];
			}
		}
		if(summ==0)
		{
			vd[d[u]]--;
			if(!vd[d[u]])
				d[s]=n;
			d[u]=id+1;
			vd[d[u]]++;
		}
			
		return summ;
	}
	long long isap(int S,int T)
	{
		memset(d,0,sizeof(d));
		memset(vd,0,sizeof(vd));
		s=S;
		t=T;
		vd[0]=n;
		maxx=0;
		while(d[s]<n)
			maxx+=dfs(s,(1ll<<40)/2);
		return maxx;
	}
}g;
bool check(long long t)
{
	g.init();
	for(int i=0;i<g.g[n-1].size();i++)
		g.c[n-1][g.g[n-1][i]]*=t;
	long long ans=g.isap(n-1,n);
	//printf("%lld\n",ans);
	for(int i=0;i<g.g[n-1].size();i++)
		g.c[n-1][g.g[n-1][i]]/=t;
	if(ans>=summ)
		return 1;
	return 0;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=m;i++)
		cin>>b[i];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>k[i][j];
	n=n+m+2;
	l=0;
	r=(1ll<<40);
	while(l+1<r)
	{
		mid=(l+r)/2;
		if(check(mid))
			r=mid;
		else
			l=mid;
	}
	printf("%.6lf\n",r*1.0/10000);
	return 0;
}
posted @ 2019-12-11 21:36  loney_s  阅读(111)  评论(0)    收藏  举报