zoukankan      html  css  js  c++  java
  • 【洛谷 P3705】 [SDOI2017]新生舞会(费用流,01分数规划)

    题目链接
    看到这题我想到了以前做过的一题,名字记不清了,反正里面有“矩阵”二字,然后是道二分图匹配的题。
    经典的行列连边网络流。
    (i)行和第(j)列连边,费用为(b[i][j]-a[i][j] imes mid),源点连行,列连汇点,跑最小费用最大流得到的最小费用取负,这个值就是最大的(sum a[i][j]-b[i][j] imes mid),于是愉快的二分。
    其实费用取反跑最小费用最大流再取反就是最大费用最大流。为什么不直接写最大费用最大流?我写WA了?反正就是错了。血的教训

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #define INF 2147483647
    using namespace std;
    const int MAXN = 500;
    const int MAXM = 200010;
    const double eps = 1e-8;
    queue <int> q; 
    int s, t, now, n;
    struct Edge{
        int from, next, to, rest;
        double cost;
    }e[MAXM];
    int head[MAXN], num = 1, vis[MAXN], Flow[MAXN], pre[MAXN], a[110][110], b[110][110], o;
    double l, r, mid, dis[MAXN];
    inline void Add(int from, int to, int flow, double cost){
        e[++num] = (Edge){ from, head[from], to, flow, cost }; head[from] = num;
        e[++num] = (Edge){ to, head[to], from, 0, -cost }; head[to] = num;
    }
    int RoadsExist(){
        q.push(s);
        for(int i = 1; i <= o; ++i)
           dis[i] = 1e10, pre[i] = 0;
        dis[s] = 0; Flow[s] = INF;
        while(!q.empty()){
          now = q.front(); q.pop(); vis[now] = 0;
          for(int i = head[now]; i; i = e[i].next)
             if(e[i].rest && dis[e[i].to] > dis[now] + e[i].cost){
               dis[e[i].to] = dis[now] + e[i].cost;
               pre[e[i].to] = i;
               Flow[e[i].to] = min(Flow[now], e[i].rest);
               if(!vis[e[i].to]){
                 vis[e[i].to] = 1;
                 q.push(e[i].to);
               }
             }
        }
        return pre[t];
    }
    double ek(){
    	double maxcost = 0;
    	while(RoadsExist()){
          maxcost += (double)Flow[t] * dis[t];
          for(int i = t; i != s; i = e[pre[i]].from){
             e[pre[i]].rest -= Flow[t];
             e[pre[i] ^ 1].rest += Flow[t];
          }
        }
        return -maxcost;
    }
    int main(){
        scanf("%d", &n); o = (n << 1) + 2; s = o - 1; t = o;
        for(int i = 1; i <= n; ++i)
           for(int j = 1; j <= n; ++j){
              scanf("%d", &a[i][j]);
              r += a[i][j];
           }
        for(int i = 1; i <= n; ++i)
           for(int j = 1; j <= n; ++j)
              scanf("%d", &b[i][j]);
        while(r - l > eps){
        	num = 1; mid = (l + r) / 2.0;
        	for(int i = 1; i <= o; ++i) head[i] = 0;
        	for(int i = 1; i <= n; ++i){
        	   Add(s, i, 1, 0), Add(i + n, t, 1, 0);
        	   for(int j = 1; j <= n; ++j)
        	      Add(i, j + n, 1, -(double)a[i][j] + (double)b[i][j] * mid);
        	}
        	if(ek() <= 0) r = mid;
        	else l = mid;
        }
        printf("%.6lf
    ", l);
        return 0;
    }
    
    
  • 相关阅读:
    剑指offer39-平衡二叉树
    剑指offer37-数字在排序数组中出现的次数
    剑指offer36-两个链表的第一个公共结点
    剑指offer31-整数中1出现的次数
    剑指offer30-连续子数组的最大和
    剑指offer28-数组中出现次数超过一半的数字
    剑指offer26-二叉搜索树与双向链表
    剑指offer21-栈的压入、弹出序列
    剑指offer16-合并两个排序的链表
    C#-杂碎
  • 原文地址:https://www.cnblogs.com/Qihoo360/p/10360967.html
Copyright © 2011-2022 走看看