bzoj 4819: [Sdoi2017]新生舞会
题目链接
题解
很裸的01分规,化完式子大概是这样的
$ \sum ai - \sum bi *C$
二分最大比值C建图,
当最大费用 >=0 时,二分下界调大
否则上界调小
在洛谷似乎有些卡常,但借助bzoj的总时限还是跑过去了
然而开始的时候我是用$ \sum bi *C-\sum ai $这个式子做的
然后GG...调不动了.....重打
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define eps 1e-7
using std::queue;
inline int read() {
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
while (c<='9'&&c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
const int maxn = 307;
int n;
double a[maxn][maxn];
double b[maxn][maxn];
struct node{
int flow,u,v,next;double cost;
}edge[maxn*maxn*3];
int num=1,head[maxn<<1],S,T,tmp;
inline void add_edge(int u,int v,int flow,double cost) {
edge[++num].u = u;edge[num].v = v;edge[num].flow = flow;edge[num].cost = cost,edge[num].next = head[u];head[u] = num;
}
inline void ADD(int u,int v,int flow,double cost) { add_edge(u,v,flow,cost); add_edge(v,u,0,-cost); }
void rebuild(double C) {
num=1;
memset(head,0,sizeof head);
for (int i = 1;i <= n;++i)
ADD(S,i,1,0), ADD(n + i,T,1,0);
for (int i = 1;i <= n;++ i) for (int j = 1;j <= n;++ j)
ADD(i,n + j,1, a[i][j]-b[i][j]*C);
}
double dis[maxn<<2];int pre[maxn<<2];bool vis[maxn<<2];
bool spfa() {
queue<int>q;
for(int i=S;i<=T;++i) dis[i]=-0x3f3f3f3f,pre[i]=0;
memset(vis,0,sizeof vis);
q.push(S);vis[S] = true;dis[S] = 0;
while (!q.empty()) {
int u = q.front();q.pop();
for (int v,i = head[u];i;i = edge[i].next) {
v = edge[i].v;
if (edge[i].flow > 0&&dis[v] < dis[u] + edge[i].cost) {
pre[v] = i;
dis[v] = dis[u] + edge[i].cost;
if (!vis[v]) q.push(v),vis[v] = 1;
}
}
vis[u] = 0;
}
return dis[T] != -0x3f3f3f3f;
}
double calc() {
double ret = 0;int MF = 0x3f3f3f3f;
for (int i = T;i != S;i = edge[pre[i]].u)
MF = std::min(edge[pre[i]].flow,MF);
for (int i = T;i != S;i = edge[pre[i]].u) {
edge[pre[i]].flow -= MF;
edge[pre[i]^1].flow += MF;
ret += edge[pre[i]].cost * 1.0*MF;
}
return ret;
}
double mfmc() {
double ret=0;
while(spfa())
ret += calc();
return ret;
}
bool check() { return mfmc()>=0 ? true : false ; }
int main() {
n = read();
S = 0,T = n*2+1;
for (int i = 1;i <= n;++ i) for (int j = 1;j <= n;++ j)
a[i][j]=read();
for (int i = 1;i <= n;++ i) for (int j = 1;j <= n;++ j)
b[i][j] = read();
//printf("%lf %lf\n",dis[0],0x3f3f3f3f);
double l=0,r=1000009.0;
while(r - l > eps) {
double mid = (l + r) / 2;
rebuild(mid);
if(check()) l=mid;
else r=mid;
}
printf("%lf\n",l);
return 0;
}