二分+网络流
二分时间mid,每次从新建图跑一遍看最大流是否大于等于庄家的总和,最后不断逼近答案
装甲到汇点建装甲值的边,源点到武器建mid*武器伤害的边,武器到装甲建无限大的边
(除了主函数和check函数外,其余都是kuangbin的板子,改了一下关键字)
#include <bits/stdc++.h>
#define eps 1e-8
using namespace std;
const int MAXN = 300000;
const int MAXM = 1000000;
const int INF = 0x3f3f3f3f;
int N,M;
double A[MAXN],B[MAXM];
struct Edge
{
int from,to,next;
double cap,flow,cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN];
double dis[MAXN];
bool vis[MAXN];
void addedge(int u,int v,double cap,double cost)
{
edge[tol].from=u;
edge[tol].to = v;
edge[tol].cap=cap;
edge[tol].cost = cost;
edge[tol].flow = 0;
edge[tol].next = head[u];
head[u] = tol++;
edge[tol].from=v;
edge[tol].to = u;
edge[tol].cap = 0;
edge[tol].cost = -cost;
edge[tol].flow = 0;
edge[tol].next = head[v];
head[v] = tol++;
}
bool spfa(int s,int t)
{
queue<int>q;
for(int i = 0; i <= N+M+1; i++)
{
dis[i] = (double)INF;
vis[i] = false;
pre[i] = -1;
}
dis[s] = 0.0;
vis[s] = true;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost )
{
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if(!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
if(pre[t] == -1)
return false;
else
return true;
}
//返回的是最大流,cost 存的是最小费用
double minCostMaxflow(int s,int t,double &cost)
{
double flow = 0.0;
cost = 0.0;
while(spfa(s,t))
{
double Min = (double)INF;
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
if(Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap - edge[i].flow;
}
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += edge[i].cost * Min;
}
flow += Min;
}
return flow;
}
bool check(double mid,int s,int t,double sum)
{
for(int i=0;i<tol;i++)edge[i].flow=0.0;
for(int i=head[s];i!=-1;i=edge[i].next)
edge[i].cap=B[edge[i].to-N]*mid;
double mincost;
double maxflow=minCostMaxflow(s,t,mincost);
//printf("maxflow=%lf\n",maxflow);
if(fabs(maxflow-sum)<eps)return true;
return false;
}
int main()
{
cin>>N>>M;
int s,t;
double sum=0.0;
s=0,t=N+M+1;
memset(head,-1,sizeof(head));
for(int i=1;i<=N;i++)
{
cin>>A[i];
sum+=A[i];
addedge(i,t,A[i],0.0);
}
for(int i=1;i<=M;i++)
{
cin>>B[i];
addedge(s,i+N,B[i],0.0);
}
for(int i=1;i<=M;i++)
{
for(int j=1;j<=N;j++)
{
int x;
cin>>x;
if(x)addedge(i+N,j,(double)INF,0.0);
}
}
double l,r,mid,ans=0.0;
l=0.0,r=5000000.0;
while(r-l>eps)
{
mid=(l+r)/2.0;
if(check(mid,s,t,sum))ans=mid,r=mid;
else l=mid;
}
printf("%lf\n",ans);
return 0;
}