【BZOJ3993】星际战争(SDOI2015)-二分答案+最大流

测试地址:星际战争

做法:注意到答案具有单调性,答案比某一个点大时都有可行解,比这个点小时都没有可行解,于是可以二分答案,转变为判定性问题:在某一个时间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;
}


posted @ 2017-03-23 18:37  Maxwei_wzj  阅读(132)  评论(0编辑  收藏  举报