[SDOI2017]新生舞会 0/1分数规划

~~~题面~~~

题解:

0/1分数规划,,,但是竟然有诡异的精度问题???因为这个被卡了好久

中途还写过一次KM,,,结果陷入死循环,,,我大概是写了一个假KM,,,于是放弃KM,回来调费用流

这个题面也很直白啊~~~

我们令C>=x,

然后二分求出最大的x即可,

每次跑费用流前重新定义边权

a[i] - mid * b[i]

然后跑最大费用最大流看看跑出来能不能有大于0的费用就可以了

 #include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 410
#define ac 40000
#define eps 1e-7
#define inf -9000000000LL
#define getchar() *o++
#define D printf("line in %d\n",__LINE__);
char READ[],*o=READ;
int n,s,t;
double maxn,mid,ans;
int tmp[AC][AC],last[AC];
int Head[AC],Next[ac],date[ac],haveflow[ac],disflow[AC],tot=;
double dis[ac],cost[ac],a[ac],b[ac];
bool z[AC];
deque <int> q;
/*01分数规划,二分x,然后跑最大费用最大流,看费用是否大于等于0,大于等于0即合法。
1 ~ n 为女生,n+1 ~ 2*n 为男生 ,s = 2*n+1 ,t = 2*n+2*/ inline int read()
{
int x=;char c=getchar();
while(c > '' || c < '') c=getchar();
while(c >= '' && c <='') x=x*+c-'',c=getchar();
return x;
} inline void add(int f,int w,int aa,int bb)
{
date[++tot]=w,Next[tot]=Head[f],Head[f]=tot,a[tot]=aa,b[tot]=bb;
date[++tot]=f,Next[tot]=Head[w],Head[w]=tot;
} inline void upmax(double &a,double b)
{
if(b > a) a = b;
} void pre()
{
int a;
n=read();
s= * n + ,t= * n + ;
for(R i=;i<=n;i++)
for(R j=;j<=n;j++)
tmp[i][j]=read();
for(R i=;i<=n;i++)
for(R j=;j<=n;j++)
{
a=read();
add(i,n + j,tmp[i][j],a);
upmax(maxn,(double)tmp[i][j] / (double)a);
}
for(R i=;i<=n;i++) add(s,i,,);
for(R i=n+;i<=*n;i++) add(i,t,,);
} void aru()
{
int x=t;
while(x != s)
{
haveflow[last[x]] -= disflow[x];//是减掉disflow[x]啊
haveflow[last[x] ^ ] += disflow[x];
x=date[last[x] ^ ];
}
ans += disflow[t] * dis[t]; } bool spfa()
{
int x,now;
z[s]=true,dis[s]=,disflow[s]=INT_MAX;
q.push_front(s);
while(!q.empty())
{
x=q.front();
q.pop_front();
z[x]=false;
for(R i=Head[x]; i ;i=Next[i])
{
now=date[i];
if(haveflow[i] && dis[now] < dis[x] + cost[i])
{
dis[now] = dis[x] + cost[i];
disflow[now] = min(haveflow[i],disflow[x]);//是用当前边!的haveflow和当前点!的disflow的比较
last[now] = i;
if(!z[now] && now != t)//t当然不能进来了
{
if(!q.empty() && dis[now] > dis[q.front()]) q.push_front(now);
else q.push_back(now);
z[now]=true;
}
}
}
}
x=t;
if(dis[t] != inf) aru();
return dis[t] != inf;
} bool check()
{
int bb=n * + ;
for(R i=;i<=bb;i++) dis[i]=inf;//要手动inf,,error!!!怎么可以改了下面不改这里,,,
ans=;
for(R i=;i<=tot;i+=)//枚举正向边
{
haveflow[i] = ;//所有边默认流量都为1
haveflow[i+] = ;//反向边当然是0
cost[i] = a[i] - mid * b[i];
cost[i+] = -cost[i];//重新建图
}
while(spfa()) //貌似因为是double,所以memset 128不好用了
for(R i=;i<=bb;i++) dis[i] = inf;
return ans >= ;
} void half()
{
double l=,r=;
while(r - l > eps)
{
mid = (l + r) / ;//因为是实数,所以没有什么偏向问题
if(check()) l = mid;
else r = mid;//error!!!!!!!!所以说是因为-eps才错的?
}//啊啊啊啊啊啊啊啊啊啊那为什么不能减啊!!!!!!!!
printf("%.6lf\n",l);
} int main()
{
// freopen("in.in","r",stdin);
fread(READ,,,stdin);
pre();
half();
// fclose(stdin);
return ;
}
上一篇:Bzoj4753/洛谷P4432 [JSOI2016]最佳团体(0/1分数规划+树形DP)


下一篇:查看Linux版本系统信息方法汇总