测试地址:星际战争
做法:注意到答案具有单调性,答案比某一个点大时都有可行解,比这个点小时都没有可行解,于是可以二分答案,转变为判定性问题:在某一个时间t内能否消灭所有敌人?再看这个题目的模型,注意到在时间t内一个武器最多能够削减(t*攻击速度)个单位的装甲值,我们要做的就是把每个武器的攻击力合理分配来消灭所有敌人,这和网络流的模型非常相似,所以我们从源点S向每个武器连接一条边,容量为该时间内该武器最多的输出,再从每个敌人向汇点T连接一条边,容量为该敌人的装甲值,对于武器i,如果它能攻击敌人j,那么从武器i向敌人j连一条容量无限的边。建完图后,求出这个网络的最大流,如果最大流的流量等于所有敌人的装甲值之和,则敌人就都可以被消灭,否则敌人就不可以被全部消灭。对于每个二分到的答案重新建图跑最大流,如果可行则继续二分左半区间,不可行则继续二分右半区间,注意浮点数的相等判断即可。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#define eps 1e-8
#define inf 1000000000
using namespace std;
int n,m,first[2510]={0},s=0,t,tot,level[2510];
double a[55],b[55],l,r,mid,sum,finalans;
int att[55][55];
struct edge {int v,next;double f;} e[100010];
void insert(int a,int b,double f)
{
e[++tot].v=b,e[tot].f=f,e[tot].next=first[a],first[a]=tot;
}
bool equal(double a,double b)
{
return fabs(a-b)<=eps;
}
bool makelevel()
{
queue<int> q;
memset(level,-1,sizeof(level));
level[s]=1;
q.push(s);
while(!q.empty())
{
int v=q.front();q.pop();
if (v==t) return 1;
for(int i=first[v];i;i=e[i].next)
if (level[e[i].v]==-1&&!equal(e[i].f,0))
{
level[e[i].v]=level[v]+1;
q.push(e[i].v);
}
}
return 0;
}
double dfs(int v,double maxf)
{
double ret=0,f;
if (v==t) return maxf;
for(int i=first[v];i;i=e[i].next)
if (!equal(e[i].f,0)&&level[e[i].v]==level[v]+1)
{
f=dfs(e[i].v,min(maxf-ret,e[i].f));
e[i].f-=f;
e[i^1].f+=f;
ret+=f;
if (equal(ret,maxf)) return ret;
}
return ret;
}
bool check(double val)
{
tot=1;
memset(first,0,sizeof(first));
for(int i=1;i<=m;i++)
insert(s,i,mid*b[i]),insert(i,s,0);
for(int i=1;i<=n;i++)
insert(m+i,t,a[i]),insert(t,m+i,0);
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
if (att[i][j]) insert(i,m+j,inf),insert(m+j,i,0);
double ans=0;
while(makelevel())
{
ans+=dfs(s,inf);
}
return equal(ans,sum);
}
int main()
{
scanf("%d%d",&n,&m);
t=n+m+1;
for(int i=1;i<=n;i++)
{
scanf("%lf",&a[i]);
sum+=a[i];
}
for(int i=1;i<=m;i++)
scanf("%lf",&b[i]);
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
scanf("%d",&att[i][j]);
l=0;r=sum;
while(l<=r)
{
mid=(l+r)/2;
if (check(mid))
{
r=mid-eps;
finalans=mid;
}
else l=mid+eps;
}
printf("%.6lf",finalans);
return 0;
}