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. 汉化manjaro下的火狐浏览器

    1.下载 汉化包 sudo pacman -S firefox-i18n-zh-cn 2.查看add-ons下的language选项有没有已安装的包 3.在浏览器的地址栏输入 搜索intl.local ...

  2. shell 输出带颜色字体

    输出特效格式控制:\033[0m  关闭所有属性  \033[1m   设置高亮度  \03[4m   下划线  \033[5m   闪烁  \033[7m   反显  \033[8m   消隐  \ ...

  3. js 关于字符串转数字及数字保留位数的控制

    1.parseInt()和parseFloat()两个转换函数,将字符串转换成相应的数字. 1.parseInt() parseInt进行转换时,将字符串转成相应的整数.浮点数以后的数字都不要了. p ...

  4. php-7.2.3源代码和php-5.6.26源代码摘录,对比 “汇编php文件”和“执行opcode代码”

    php-7.2.3 在“汇编php文件”和“执行opcode代码”上做了大量改变php-5.6.26 没见到支持抽象语法树的相关代码,php-7.2.3 见到支持抽象语法树的相关代码php-5.6.2 ...

  5. Python学习 :文件操作

    文件基本操作流程: 一. 创建文件对象 二. 调用文件方法进行操作 三. 关闭文件(注意:只有在关闭文件后,才会写入数据) fh = open('李白诗句','w',encoding='utf-8') ...

  6. HTTP学习之HTTP基础

    学习HTTP技术,首先要了解它的在web通信中有哪些特点,起到什么作用.有哪些规范.都有什么功能. HTTP的特点 HTTP使用的是一种可靠的.快速响应的数据传输协议,用户一旦发起请求,Web服务器可 ...

  7. 安装 Node.js v8.0 生产环境

    步骤://center os 上把apt-get换成yum 第一步:进入服务器升级工具包 sudo apt-get update 第二步:安装git  sudo apt-get install git ...

  8. R语言学习笔记(十七):data.table包中melt与dcast函数的使用

    melt函数可以将宽数据转化为长数据 dcast函数可以将长数据转化为宽数据 > DT = fread("melt_default.csv") > DT family_ ...

  9. wamp调用ICE中间件

    wamp调用ICE中间件 wamp 是集成开发包,我的wamp中的php 为5.3.10 ,经过3天艰苦奋战,终于在phpinfo()中看到了 ICE 出现了.. OK,最新的ice  为 3.5.1 ...

  10. 为什么我要放弃javaScript数据结构与算法(第二章)—— 数组

    第二章 数组 几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构.JavaScript里也有数组类型,虽然它的第一个版本并没有支持数组.本章将深入学习数组数据结构和它的能力. 为什么 ...