01分数规划:通常的问法是:在一张有 \(n\) 个点,\(m\) 条边的有向图中,每一条边均有其价值 \(v\) 与其代价 \(w\);求在图中的一个环使得这个环上所有的路径的权值和与代价和的比率最小\大。即求 \(\frac{\sum v}{\sum w}\) 的最小值\最大值。

  通常的解法也是比较固定的,我们首先假设求最大值,最优的答案为 \(L\),\(L = \frac{\sum v}{\sum w}\)。接下来我们对于这个式子进行变形:

\(L * \sum w = \sum v\)

\(L * \sum w - \sum v = 0\)

  注意到这里面的 \(L\) 是我们假设出来的最优解。若我们二分这个答案,设我们当前二分出来的答案为 \(R\),则考虑当 \(R < L\) 和 \(R > L\) 的时候会分别出现什么状况。当我们选定了一个 \(R\),一条边的 \(R * w - v\) 就是一个固定的值,不妨将它视作新的边权。而原式便是这些边权的和。若此时图中有负环,因为 \(R * w - v\) 随 \(R\) 的增大单调不减,说明可以将 \(R\) 的值设定得更大,有更有的解。存在零环说明此时的 \(R\) 恰好等于 \(L\),没有负环 & 零环说明不能取到当前值。

1.HNOI2009最小圈

  裸的01分数规划,只是最大变成了最小。做法还是一样,将spfa转化为最长路判正环即可。

#include <bits/stdc++.h>
using namespace std;
#define maxn 10050
#define eps 0.0000000001
#define db double
int n, m, cnp = , head[maxn];
db ans, dis[maxn];
bool vis[maxn], mark[maxn]; struct edge
{
int to, last;
db co, w;
}E[maxn]; void add(int u, int v, db w)
{
E[cnp].to = v, E[cnp].w = w;
E[cnp].last = head[u], head[u] = cnp ++;
} bool spfa(int u)
{
vis[u] = , mark[u] = ;
for(int i = head[u]; i; i = E[i].last)
{
int v = E[i].to;
if(dis[v] < dis[u] + E[i].co)
{
dis[v] = dis[u] + E[i].co;
if(vis[v] || spfa(v)) { vis[u] = ; return ; }
}
}
vis[u] = ;
return ;
} bool check()
{
memset(mark, , sizeof(mark));
memset(dis, , sizeof(dis));
for(int i = ; i <= n; i ++)
if(!mark[i] && spfa(i)) return ;
return ;
} int main()
{
scanf("%d%d", &n, &m);
for(int i = ; i <= m; i ++)
{
int u, v; db w;
scanf("%d%d%lf", &u, &v, &w);
add(u, v, w);
}
db l = -, r = ;
while(l + eps < r)
{
db mid = (l + r) / 2.0;
for(int i = ; i < cnp; i ++) E[i].co = mid - E[i].w;
if(check()) ans = mid, r = mid;
else l = mid;
}
printf("%.8lf\n", ans);
return ;
}

2.APIO2017商旅

  其实也是裸裸的一道题,建图的方式略有隐藏罢了。注意到两个点之间只能携带一种物品,我们将这些路径建成边连起来,然后套路即可。只不过因为这题我二分的是整数,所以要注意判零环。

#include <bits/stdc++.h>
using namespace std;
#define maxn 105
#define INF 99999999
int n, m, T, a[maxn][maxn * ][];
int R[maxn][maxn], val[maxn][maxn];
int ans, E[maxn][maxn], dis[maxn];
bool mark[maxn], vis[maxn]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} void Floyd()
{
for(int k = ; k <= n; k ++)
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
R[i][j] = min(R[i][j], R[i][k] + R[k][j]);
} void Build()
{
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
{
if(R[i][j] >= INF) continue;
for(int k = ; k <= T; k ++)
if(~a[i][k][] && ~a[j][k][])
{
int v = a[j][k][] - a[i][k][];
if(v > val[i][j]) val[i][j] = v;
}
}
} bool spfa(int u)
{
vis[u] = , mark[u] = ;
for(int i = ; i <= n; i ++)
{
if(i == u || R[i][u] >= INF) continue;
if(dis[i] > dis[u] + E[i][u] || (!mark[i] && dis[i] == dis[u] + E[i][u]))
{
dis[i] = dis[u] + E[i][u];
if(vis[i] || spfa(i)) { vis[u] = ; return ;}
}
else if(dis[i] == dis[u] + E[i][u] && vis[i])
{ vis[u] = ; return ; }
}
vis[u] = ;
return ;
} bool check()
{
memset(dis, , sizeof(dis));
memset(mark, , sizeof(mark));
for(int i = ; i <= n; i ++)
if(!mark[i] && spfa(i)) return ;
return ;
} void work()
{
int l = , r = ;
while(l <= r)
{
int mid = (l + r) >> ;
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
{
if(R[i][j] >= INF) continue;
E[i][j] = mid * R[i][j] - val[i][j];
}
if(check()) ans = mid, l = mid + ;
else r = mid - ;
}
} int main()
{
n = read(), m = read(), T = read();
for(int i = ; i <= n; i ++)
for(int j = ; j <= * T; j ++)
if(j & ) a[i][(j >> ) + ][] = read();
else a[i][j >> ][] = read();
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
if(i != j) R[i][j] = INF;
for(int i = ; i <= m; i ++)
{
int x = read(), y = read(), t = read();
R[x][y] = min(R[x][y], t);
}
Floyd();
Build();
work();
printf("%d\n", ans);
return ;
}

3.SDOI2017新生舞会

  就套路吧……只不过要注意下精度。【我的代码跑得非常非常的慢……】

#include <bits/stdc++.h>
using namespace std;
#define maxn 450
#define INF 9999999999.9
#define int long long
#define db double
#define eps 0.00000001 int n, a[maxn][maxn], b[maxn][maxn];
int flow[maxn], S, T, pre[maxn];
int cnp, head[maxn];
deque <int> q; bool vis[maxn], flag;
db dis[maxn], cost; struct edge
{
int to, last, u, f, F;
db co;
}E[maxn * ]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} void add(int u, int v, int f, int co)
{
E[cnp].u = u, E[cnp].to = v, E[cnp].co = (db) co;
E[cnp].last = head[u]; E[cnp].f = E[cnp].F = f; head[u] = cnp ++;
E[cnp].u = v, E[cnp].to = u, E[cnp].co = -(db) co;
E[cnp].last = head[v]; E[cnp].f = E[cnp].F = ; head[v] = cnp ++;
} void init()
{
int a = * n + ;
for(int i = ; i <= a; i ++) dis[i] = INF;
} bool SPFA()
{
q.push_back(S); init();
flow[S] = INF, vis[S] = , dis[S] = ;
while(!q.empty())
{
int u = q.front(); q.pop_front(); vis[u] = ;
for(int i = head[u]; ~i; i = E[i].last)
{
int v = E[i].to;
if(E[i].f && dis[v] > dis[u] + E[i].co)
{
dis[v] = dis[u] + E[i].co; pre[v] = i;
flow[v] = min(flow[u], E[i].f);
if(!vis[v])
{
vis[v] = ;
if(!q.empty() && dis[v] < dis[q.front()]) q.push_front(v);
else q.push_back(v);
}
}
}
}
if(dis[T] >= INF) return ; else return ;
} db work()
{
cost = ; int ans = ;
while(SPFA())
{
int e = pre[T];
while()
{
E[e].f -= flow[T], E[e ^ ].f += flow[T];
if(E[e].u != S) e = pre[E[e].u];
else break;
}
ans += flow[T]; cost += (db) flow[T] * dis[T];
}
return cost;
} signed main()
{
n = read(); T = * n + ;
memset(head, -, sizeof(head));
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
a[i][j] = read();
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
b[i][j] = read();
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
add(i, j + n, , );
for(int i = ; i <= n; i ++)
{
add(i + n, T, , );
add(S, i, , );
}
db r = 10000.0, l = eps, ans = ;
while(r - l > eps)
{
db mid = (r + l) / 2.0;
for(int i = ; i < cnp; i += )
{
int u = E[i].u, v = E[i].to; E[i].f = E[i].F, E[i ^ ].f = E[i ^ ].F;
if(!u || !v || u == * n + || v == * n + ) continue;
E[i].co = -((db) a[u][v - n] - mid * (db) b[u][v - n]);
E[i ^ ].co = - E[i].co;
}
db mf = work();
if(mf > ) r = mid;
else ans = mid, l = mid;
}
printf("%.6lf\n", ans);
return ;
}

【算法】01分数规划 --- HNOI2009最小圈 & APIO2017商旅 & SDOI2017新生舞会的更多相关文章

  1. 【bzoj 3232】圈地游戏(算法效率--01分数规划+图论--最小割)

    题目:DZY家的后院有一块地,由N行M列的方格组成,格子内种的菜有一定的价值,并且每一条单位长度的格线有一定的费用.DZY喜欢在地里散步.他总是从任意一个格点出发,沿着格线行走直到回到出发点,且在行走 ...

  2. 最大密集子图(01分数规划+二分+最小割)POJ3155

    题意:给出一副连通图,求出一个子图令g=sigma(E)/sigma(V); h[g]=sigma(E)-g*sigma(V):设G是最优值 则当h[g]>0:g<G h[g]<0, ...

  3. BZOJ1486 HNOI2009 最小圈 【01分数规划】

    BZOJ1486 HNOI2009 最小圈 Description 应该算是01分数规划的裸板题了吧..但是第一次写还是遇到了一些困难,vis数组不清零之类的 假设一个答案成立,那么一定可以找到一个环 ...

  4. 【题解】 [HNOI2009] 最小圈 (01分数规划,二分答案,负环)

    题目背景 如果你能提供题面或者题意简述,请直接在讨论区发帖,感谢你的贡献. 题目描述 对于一张有向图,要你求图中最小圈的平均值最小是多少,即若一个圈经过k个节点,那么一个圈的平均值为圈上k条边权的和除 ...

  5. 洛谷P3199 [HNOI2009]最小圈(01分数规划)

    题意 题目链接 Sol 暴力01分数规划可过 标算应该是这个 #include<bits/stdc++.h> #define Pair pair<int, double> #d ...

  6. 2018.09.24 bzoj1486: [HNOI2009]最小圈(01分数规划+spfa判负环)

    传送门 答案只保留了6位小数WA了两次233. 这就是一个简单的01分数规划. 直接二分答案,根据图中有没有负环存在进行调整. 注意二分边界. 另外dfs版spfa判负环真心快很多. 代码: #inc ...

  7. ZOJ 2676 Network Wars ★(最小割算法介绍 && 01分数规划)

    [题意]给出一个带权无向图,求割集,且割集的平均边权最小. [分析] 先尝试着用更一般的形式重新叙述本问题.设向量w表示边的权值,令向量c=(1, 1, 1, --, 1)表示选边的代价,于是原问题等 ...

  8. BZOJ_1486_[HNOI2009]最小圈_01分数规划

    BZOJ_1486_[HNOI2009]最小圈_01分数规划 Description Input Output Sample Input 4 5 1 2 5 2 3 5 3 1 5 2 4 3 4 1 ...

  9. 【BZOJ1486】[HNOI2009]最小圈 分数规划

    [BZOJ1486][HNOI2009]最小圈 Description Input Output Sample Input 4 5 1 2 5 2 3 5 3 1 5 2 4 3 4 1 3 Samp ...

随机推荐

  1. js函数的节流和防抖

    js函数的节流和防抖 用户浏览页面时会不可避免的触发一些高频度触发事件(例如页面 scroll ,屏幕 resize,监听用户输入等),这些事件会频繁触发浏览器的重拍(reflow)和重绘(repai ...

  2. 密码发生器 南阳acm519

    密码发生器 时间限制:1000 ms  |  内存限制:65535 KB 难度:2   描述 在对银行账户等重要权限设置密码的时候,我们常常遇到这样的烦恼:如果为了好记用生日吧,容易被破解,不安全:如 ...

  3. Linux编程之Epoll高并发

    网络上所有资料都说epoll是高并发.单线程.IO重叠服用的首选架构,比select和poll性能都要好,特别是在有大量不活跃连接的情况下.具体原理就不阐述了,下面说说使用. 具有有三个函数: #in ...

  4. 多线程编程之Apue3rd_Chapter11之互斥锁_读写锁_自旋锁

    学习了apue3rd的第11章,主要讲的是多线程编程.因为线程共享进程的资源比如堆和全局变量,多线程编程最重要的是,使用各种锁进行线程同步. 线程编程首先要学习的三个函数如下: #include &l ...

  5. (数据科学学习手札21)sklearn.datasets常用功能详解

    作为Python中经典的机器学习模块,sklearn围绕着机器学习提供了很多可直接调用的机器学习算法以及很多经典的数据集,本文就对sklearn中专门用来得到已有或自定义数据集的datasets模块进 ...

  6. allegro导入网表过程中出现的错误信息

    1. 找不到焊盘PAD,下面这句话的意思是器件封装找不到焊盘46.pad WARNING(SPMHNI-): Unable to load symbol ): Could not find padst ...

  7. 数据库sql命令

    本文为转载,原文地址:http://www.cnblogs.com/cangqiongbingchen/p/4530333.html 1.说明:创建数据库CREATE DATABASE databas ...

  8. 基于Python的接口自动化-01

    为什么要做接口测试 当前互联网产品迭代速度越来越快,由之前的2-3个月到个把月,再到班车制,甚至更短,每次发版之前都需要对所有功能进行回归测试,在人力资源有限的情况下,做自动化测试很有必要.由于UI更 ...

  9. Oracle修改表字段类型(number-->varchar2(len)),亲测可用

    思路: --新建临时表以存储正确的顺序create table A_2 as select (column1,colum2,……A表中的顺序) from A_1 ; --删除表A_1drop tabl ...

  10. 【app.json】配置说明,不断更新中

    app.json文件用来对微信小程序进行全局配置,决定页面文件的路径.窗口表现.设置网络超时时间.设置多 tab 等. 注意: 1) json配置中键名.键值必须使用双引号,不能使用单引号. 2) 以 ...