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. IDEA无法引入已经创建的类

    解决办法: 清理一下缓存: File->Invalidate Caches/Restart... OVER!

  2. 使用jQuery实现数字逆时针旋转

    要实现数字逆转,最主要是分析我们页面的元素结果,结合选择器充分利用起来! 例如:以下lable中每一个id和值的安排具有一定结构的意义需要用心分析: jQuery代码:

  3. 监听浏览器返回,pushState,popstate 事件,window.history对象

    在WebApp或浏览器中,会有点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面.确认离开页面或执行一些其它操作的需求.可以使用 popstate 事件进行监听返回.后退.上一页操作. 一 ...

  4. djangorestframework怎么这么好用!

    一年前就已经用过restframework, 当时觉得这个只是给web框架打辅助的, 他能实现的我也都实现(可能没有那么好用, 嘿嘿) 但是我有一种东西叫做效率, 时间就是金钱, 别人造好的就直接用就 ...

  5. JavaSE库存管理系统项目实战

    需求分析 企业库房用于存放成品.半成品.原材料.工具等物资,一般情况下,这些物资统称为物料.库存管理常见业务包括物资的入库.出库.盘点.退货.报废以及财务核算等,业务逻辑比较复杂,库房信息系统项目开发 ...

  6. OMAPL138制作SD卡启动介质及重装Linux系统

    OMAPL138制作SD卡启动盘及重装Linux系统 手里的创龙的OMAPL138平台的系统SSH坏掉了,我重新移植了openssh还是不好使,没有办法了只能重装OMAPL138的系统了,按照创龙给的 ...

  7. 数据分析处理库Pandas——索引进阶

    Series结构 筛选数据 指定值 备注:查找出指定数值的索引和数值. 逻辑运算 备注:查找出值大于2的数据. 复合索引 DataFrame结构 显示指定列 筛选显示 备注:值小于0的显示原值,否则显 ...

  8. C语言实例解析精粹学习笔记——43(希尔排序)

    实例说明: 用希尔排序方法对数组进行排序.由于书中更关注的实例,对于原理来说有一定的解释,但是对于第一次接触的人来说可能略微有些简略.自己在草稿纸上画了好久,后来发现网上有好多很漂亮的原理图. 下面将 ...

  9. Jupyter Notebook里面使用Matplotlib画图 图表中文乱码问题

    可查看以下链接: https://blog.csdn.net/ccblogger/article/details/79613335

  10. pwa学习笔记--简介

    1. 介绍 Progressive Web App , (渐进式增强 WEB 应用) 简称 PWA ,是提升WebApp的体验的一种新方法,能给用户原生应用的体验. PWA 本质上是 Web App ...