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

~~~题面~~~

题解:

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

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

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

我们令C>=x,

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

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

a[i] - mid * b[i]

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

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 410
  5 #define ac 40000
  6 #define eps 1e-7
  7 #define inf -9000000000LL
  8 #define getchar() *o++
  9 #define D printf("line in %d
",__LINE__);
 10 char READ[5000100],*o=READ;
 11 int n,s,t;
 12 double maxn,mid,ans;
 13 int tmp[AC][AC],last[AC];
 14 int Head[AC],Next[ac],date[ac],haveflow[ac],disflow[AC],tot=1;
 15 double dis[ac],cost[ac],a[ac],b[ac];
 16 bool z[AC];
 17 deque <int> q;
 18 /*01分数规划,二分x,然后跑最大费用最大流,看费用是否大于等于0,大于等于0即合法。
 19 1 ~ n 为女生,n+1 ~ 2*n 为男生 ,s = 2*n+1 ,t = 2*n+2*/
 20 
 21 inline int read()
 22 {
 23     int x=0;char c=getchar();
 24     while(c > '9' || c < '0') c=getchar();
 25     while(c >= '0' && c <='9') x=x*10+c-'0',c=getchar();
 26     return x;
 27 }
 28 
 29 inline void add(int f,int w,int aa,int bb)
 30 {
 31     date[++tot]=w,Next[tot]=Head[f],Head[f]=tot,a[tot]=aa,b[tot]=bb;
 32     date[++tot]=f,Next[tot]=Head[w],Head[w]=tot;    
 33 }
 34 
 35 inline void upmax(double &a,double b)
 36 {
 37     if(b > a) a = b;
 38 }
 39 
 40 void pre()
 41 {
 42     int a;
 43     n=read();
 44     s=2 * n + 1,t=2 * n + 2;
 45     for(R i=1;i<=n;i++)
 46         for(R j=1;j<=n;j++)
 47             tmp[i][j]=read();
 48     for(R i=1;i<=n;i++)
 49         for(R j=1;j<=n;j++)
 50         {
 51             a=read();
 52             add(i,n + j,tmp[i][j],a);
 53             upmax(maxn,(double)tmp[i][j] / (double)a);
 54         }
 55     for(R i=1;i<=n;i++) add(s,i,0,0);
 56     for(R i=n+1;i<=2*n;i++) add(i,t,0,0);    
 57 }
 58 
 59 void aru()
 60 {
 61     int x=t;
 62     while(x != s)
 63     {
 64         haveflow[last[x]] -= disflow[x];//是减掉disflow[x]啊
 65         haveflow[last[x] ^ 1] += disflow[x];
 66         x=date[last[x] ^ 1];
 67     }
 68     ans += disflow[t] * dis[t];
 69 
 70 }
 71 
 72 bool spfa()
 73 {
 74     int x,now;
 75     z[s]=true,dis[s]=0,disflow[s]=INT_MAX;
 76     q.push_front(s);
 77     while(!q.empty())
 78     {
 79         x=q.front();
 80         q.pop_front();
 81         z[x]=false;
 82         for(R i=Head[x]; i ;i=Next[i])
 83         {
 84             now=date[i];
 85             if(haveflow[i] && dis[now] < dis[x] + cost[i])
 86             {
 87                 dis[now] = dis[x] + cost[i];
 88                 disflow[now] = min(haveflow[i],disflow[x]);//是用当前边!的haveflow和当前点!的disflow的比较
 89                 last[now] = i;
 90                 if(!z[now] && now != t)//t当然不能进来了 
 91                 {
 92                     if(!q.empty() && dis[now] > dis[q.front()])    q.push_front(now);
 93                     else q.push_back(now);
 94                     z[now]=true;
 95                 }
 96             }
 97         }
 98     }
 99     x=t;
100     if(dis[t] != inf) aru();
101     return dis[t] != inf;
102 }
103 
104 bool check()
105 {
106     int bb=n * 2 + 2;
107     for(R i=1;i<=bb;i++) dis[i]=inf;//要手动inf,,error!!!怎么可以改了下面不改这里,,,
108     ans=0;
109     for(R i=2;i<=tot;i+=2)//枚举正向边
110     {
111         haveflow[i] = 1;//所有边默认流量都为1
112         haveflow[i+1] = 0;//反向边当然是0
113         cost[i] = a[i] - mid * b[i];
114         cost[i+1] = -cost[i];//重新建图 
115     }
116     while(spfa()) //貌似因为是double,所以memset 128不好用了
117         for(R i=1;i<=bb;i++) dis[i] = inf;
118     return ans >= 0;
119 }
120 
121 void half()
122 {
123     double l=0,r=10000;
124     while(r - l > eps)
125     {
126         mid = (l + r) / 2;//因为是实数,所以没有什么偏向问题
127         if(check()) l = mid;
128         else r = mid;//error!!!!!!!!所以说是因为-eps才错的?
129     }//啊啊啊啊啊啊啊啊啊啊那为什么不能减啊!!!!!!!!
130     printf("%.6lf
",l);
131 }
132 
133 int main()
134 {
135 //    freopen("in.in","r",stdin);
136     fread(READ,1,5000000,stdin);
137     pre();
138     half();
139 //    fclose(stdin);
140     return 0;
141 }
原文地址:https://www.cnblogs.com/ww3113306/p/9145659.html