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. 【转载】在C#中主线程和子线程如何实现互相传递数据

    引用:https://blog.csdn.net/shuaihj/article/details/41316731 一.不带参数创建Thread using System; using System. ...

  2. Java实现文件的上传下载

    文件上传,下载的方法: 上传代码 /** * 文件上传.保存 * * @param mapping * @param form * @param request * @param response * ...

  3. Linux Shell常用命令(长期更新)

    #判断某个字段是否匹配指定值 awk -F"," '{if($4=="value"){print $1} else {print $0}}' file.txt ...

  4. 总结laravel假数据填充步骤

    定义好模型 xxx.php 定义好数据生成的规则 database/factories/XxxlFactory.php 写入生成数据的代码,控制好生成的数据数目,对生成后的数据做出修改 databas ...

  5. 学会了 python 的pip方法安装第三方库

    超级开心啊!!!!!!!!!!!!! win10 打开cmd Installing with get-pip.py To install pip, securely download get-pip. ...

  6. 博弈dp 以I Love this Game! POJ - 1678 为例

    写在前面的话 知识基础:一些基础的博弈论的方法,动态规划的一些知识 前言:博弈论就是一些关于策略或者游戏之间的最优解,动态规划就是对于一些状态之间转移的一些递推式(or 递归),dp分为很多很多种,比 ...

  7. anaconda 安装opencv win10

    直接在命令窗口里面运行:pip install opencv-python即可.

  8. JAVA 泛型之类型擦除

    ★ 泛型是 JDK 1.5 版本引进的概念,之前是没有泛型的概念的,但泛型代码能够很好地和之前版本的代码很好地兼容. CollectionTest.java ---编译成CollectionTest. ...

  9. java 解析xml 多命名空间问题

    先贴段有命名空间的xml吧.. <feed xmlns:im="http://itunes.apple.com/rss" xmlns="http://www.w3. ...

  10. python2.7入门---字符串

        这次咱们就来看一下python的字符串类型.首先我们要知道,字符串是 Python 中最常用的数据类型.我们可以使用引号('或")来创建字符串.创建字符串很简单,只要为变量分配一个值 ...