【分数规划】【最优比率环 & 最优比率生成树】poj3621 Sightseeing Cows && poj2728 Desert King

来源:互联网 发布:学生防网络诈骗漫画 编辑:程序博客网 时间:2024/05/16 15:38

Sightseeing Cows


题意:在一个图内 每个点有点权 每条边有边权 找出其中 总点权 / 总边权 最大的环。。

最优比率环orz。。推下公式就好了……

对于每一个环ans >= sigma(wi) / sigma(ei) 所以 ans * sigma(ei) - sigma(wi) >= 0 当且仅当环为最优时取等号

我们二分答案

当 mid < ans 时 存在至少一个环使得 mid * sigma(ei) - sigma(wi) < 0 
当 mid >= ans 时 任意环 mid * sigma(ei) - sigma(wi) >= 0

那么问题就转化成对于当前mid判断是否有负环了 spfa搞定。。


有一些细节要注意:

一条边只能归属于一个点 所以我们要规定它是有向边(我最开始加无向边就跪了。。以及。。这样的话边数组开maxM大小就行了……)
还有就是存储w的时候开double会快一些 因为减少了int转double ←


#include <cstdio>#include <iostream>#include <cstring>#include <queue>#define eps 1e-4using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}const double inf = 1e200;const int Nmax = 1005;const int Mmax = 5005;int N, M, S; double w[Nmax];struct ed{int v, next;double w;}e[Mmax * 2];int k = 1, head[Nmax];inline void adde(int u, int v, int w){e[k] = (ed) { v, head[u], (double)w };head[u] = k++;}double dis[Nmax];bool vis[Nmax];int count[Nmax];queue <int> q;bool spfa(double mid){for(int i = 1; i <= N; ++i){dis[i] = inf;vis[i] = count[i] = 0;} while(q.size()) q.pop(); q.push(S); dis[S] = 0.0;while(q.size()){int u = q.front(); q.pop();vis[u] = 0;for(int i = head[u]; i; i = e[i].next){int v = e[i].v;if(dis[u] + mid * e[i].w - w[u] < dis[v]){dis[v] = dis[u] + mid * e[i].w - w[u];++count[v]; if(count[v] > N) return 1;if(!vis[v]) { vis[v] = 1; q.push(v); }}}}return 0;}int main(){N = read(); M = read();for(int i = 1; i <= N; ++i){adde(S, i, 0);w[i] = (double)read();} for(int i = 1; i <= M; ++i){int u = read(), v = read(), w = read();adde(u, v, w); //adde(v, u, w);}double l = 0.0, r = 1e10;while(r - l > eps){double mid =  (l + r) * 0.5;if(spfa(mid)) l = mid;else r = mid;}printf("%.2f\n", l + eps);return 0;}


Desert King


题意:每个点有个坐标(x, y) 两点间距离为欧几里得距离 还有个点权 求一个生成树使 总点权/总距离 最小


【1】 二分法


最优比率生成树。。感觉比最优比率环还水一些

其实就是之前lyp的那道题……直接把代码copy过去就过了QAQ (但是lyp的数据我过不了 = =他的必须要用迭代。。我不会。。。)


对于每个生成树 ans <= sigma(w) / sigma(dis) 故 ans * sigma(dis) - sigma(w) <= 0

当且仅当ans == 0时满足条件 所以二分答案 当上式>0时减小mid 小于0时增大mid

稠密图不用kruskal……要用神奇的prim = =其实对这个算法我现在还是不是特熟练。。


(好像迭代的方法和二分没有差太多。。等有空了去看QAQ)
(以前写的代码就是比现在写的长…………)

#include <cstdio>#include <iostream>#include <cmath>#include <cstring>using namespace std;int T, N;int x[1005], y[1005], z[1005];int w[1005][1005]; double rw[1005][1005];double mmax;bool vis[1000+5];double dis[1000+5];double map[1000+5][1000+5];double sum;void prim(){memset(vis, false, sizeof(vis));sum = 0.0;for(int i = 1; i <= N; ++i) dis[i] = map[1][i];vis[1] = 1;for(int i = 1; i < N; ++i){double min = 10e100; int pos = -1;for(int j = 1; j <= N; ++j){if(!vis[j] && dis[j] < min){min = dis[j];pos = j;}}if(pos == -1) break;vis[pos] = 1;sum += min;for(int j = 1; j <= N; ++j){if(!vis[j] && map[pos][j] < dis[j]) dis[j] = map[pos][j];}}}inline int Abs(int a){ if(a >= 0) return a; return -a; }inline double getdis(int x1, int x2, int y1, int y2){return sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );}inline void getmap(){mmax = 0.0;for(int i = 1; i <= N; ++i){for(int j = i + 1; j <= N; ++j){rw[i][j] = rw[j][i] = getdis(x[i], x[j], y[i], y[j]);w[i][j] = w[j][i] = Abs(z[i] - z[j]);mmax = max(mmax, w[i][j] / rw[i][j]);}}}bool check(double l){for(int i = 1; i <= N; ++i){for(int j = i + 1; j <= N; ++j){map[j][i] = map[i][j] = w[i][j] - l * rw[i][j];}}prim(); return sum >= 0;}int main(){while(scanf("%d", &N) && N){for(int i = 1; i <= N; ++i) {scanf("%d%d%d", &x[i], &y[i], &z[i]);}        getmap();        double L = 0.0, R = mmax;    while(R - L > 0.00005)    {    double mid = (L + R) / 2;    if(check(mid)) L = mid;    else R = mid;    }printf("%.3f\n", (L + R) / 2); }return 0;}

【2】Dinkelbach迭代


之前做这道题的时候硬是没看懂这个迭代。。其实是看的资料太高端的原因 = =

思路挺简单的。。最开始随便整个k然后照着之前的公式算一遍比率 然后再把算出来的比率作为k继续算 不断逼近答案。。。

然后。。。然后就没了……


#include <cstdio>#include <iostream>#include <cstring>#include <cstdlib>#include <cmath>#define eps 1e-5using namespace std;int read(){int sign = 1, n = 0; char c = getchar();while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }return sign*n;}const double inf = 1e200;const int Nmax = 1003; int N;int x[Nmax], y[Nmax], z[Nmax], near[Nmax]; double dis[Nmax][Nmax], w[Nmax][Nmax], mmin[Nmax];double prim(double temp){double sum_dis = 0.0, sum_w = 0.0; for(int i = 1; i <= N; ++i){near[i] = 1;mmin[i] = w[1][i] - dis[1][i] * temp;}near[1] = -1;for(int i = 1; i < N; ++i) // the ith edge {double Minn = inf; int pos = -1;for(int j = 1; j <= N; ++j) if(~near[j] && mmin[j] < Minn){Minn = mmin[j];pos = j;}if(pos == -1) break;sum_dis += dis[near[pos]][pos];sum_w += w[near[pos]][pos];near[pos] = -1;for(int j = 1; j <= N; ++j){double rw = w[pos][j] - dis[pos][j] * temp;if(~near[j] && mmin[j] > rw){mmin[j] = rw;near[j] = pos;}}}return sum_w / sum_dis;}inline double get_dis(int x1, int x2, int y1, int y2){ return sqrt((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2)); }inline void get_map(){for(int i = 1; i <= N; ++i){for(int j = i + 1; j <= N; ++j){dis[i][j] = dis[j][i] = get_dis(x[i], x[j], y[i], y[j]);w[i][j] = w[j][i] = fabs((double)z[i] - z[j]);}}}int main(){while(scanf("%d", &N) && N){for(int i = 1; i <= N; ++i) {x[i] = read(); y[i] = read(); z[i] = read();}get_map();double a = 0.0, b;for( ; ; a = b){b = prim(a);if(fabs(a - b) < eps) break;}printf("%.3f\n", a + eps);}return 0;}




0 0
原创粉丝点击