• 洛谷 P1730 最小密度路径


    Description

    给出一张有 \(N\) 个点 \(M\) 条边的加权有向无环图,接下来有 \(Q\) 个询问,每个询问包括 \(2\) 个节点 \(X\)\(Y\),要求算出从 \(X\)\(Y\) 的一条路径,使得密度最小(密度的定义为,路径上边的权值和除以边的数量),若无法到达输出 "\(OMG!\)"。

    Constraints

    \(1 \le N \le 50\)\(1 \le M \le 1000\)\(1\le W \le 10^5\)\(1 \le Q \le 10^5\)

    Solution

    考虑把密度的表达式表示出来,设最后答案密度为 \(r\) ,边数量为 \(e\),边权为 \(w[1] , w[2] , w[3] , ... w[e]\),则可列出等式

    \(\qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \cfrac{\sum_{i=1}^e w[i]}{e} - r = 0\)

    则通分得

    \(\qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \cfrac{\sum_{i=1}^e w[i] - e \times r}{e} = 0\)

    合并后则得

    \(\qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \cfrac{\sum_{i=1}^e (w[i] - r)}{e} = 0\)

    \(e\) 也不为 \(0\) 所以

    \(\qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \sum_{i=1}^e (w[i] - r) = 0\)

    现在可以看出,影响该式值的就是 \((w[i] - r)\)

    由于问的是最小值,可以考虑二分其中的 \(r\),根据上式,把图中每条边都减去二分的 \(r\) 值,求出最短路判断上式的值与0的关系,若大于 \(0\) 则说明取小了,找右半区间,否则找左半区间即可。

    注意这题的询问次数很多,而点数 \(n\) 与边数 \(m\) 乘积很小,所以要先预处理每一个点对的结果,在询问时 \(O(1)\) 回答询问。

    若不预处理最差复杂度 \((80pts)\)\(O(Qnmlogn)\)

    预处理最差复杂度 \((100pts)\)\(O({n^{2}}{m^{2}}logn)\)

    Code:

    // by youyou2007 in 2022.
    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <stack>
    #include <map>
    #define REP(i, x, y) for(int i = x; i < y; i++)
    #define rep(i, x, y) for(int i = x; i <= y; i++)
    #define PER(i, x, y) for(int i = x; i > y; i--)
    #define per(i, x, y) for(int i = x; i >= y; i--)
    #define lc (k << 1)
    #define rc (k << 1 | 1)
    using namespace std;
    const int N = 55;
    int n, m;
    int Q;
    struct node{
    	int v;
    	double w;
    };
    vector <node> g[N], g2[N];
    queue <int> que;
    double dis[N], ans[N][N];
    int b[N];
    double total = 0.00000;
    int read()
    {
        int x = 0, flag = 1;
        char ch = 0;
        while(!isdigit(ch)){ch = getchar(); if(ch == '-') flag = -1;}
        while(isdigit(ch)) {x = (x << 3) + ( x<< 1) + ch - '0'; ch = getchar();}
        return x*flag;
    }
    int spfa(double mid, int s, int e)//有负数,spfa求最短路
    {
    	for(int i = 1; i <= n; i++)
    	{
    		dis[i] = 99999999999.00000;
    	}
    	memset(b, 0, sizeof b);
    	dis[s] = 0.00000;
    	b[s] = 1;
    	que.push(s);
    	while(!que.empty())
    	{
    		int uu = que.front();
    		que.pop();
    		b[uu] = 0;
    		for(int i = 0; i < g[uu].size(); i++)
    		{
    			int vv = g[uu][i].v;
    			double ww = g[uu][i].w;
    			if(dis[vv] > dis[uu] + ww - mid)//这里要 - mid
    			{
    				dis[vv] = dis[uu] + ww - mid;
    				que.push(vv);
    				b[vv] = 1;
    			}
    		}
    	}
    }
    int main()
    {
    	n = read();
    	m = read();
    	rep(i, 1, m)
    	{
    		int x, y;
    		double z;
    		x = read();
    		y = read();
    		scanf("%lf", &z);
    		g[x].push_back((node){y, z});
    		total += z;
    	}
    	rep(i, 1, n)
    	{
    		rep(j, 1, n)//预处理
    		{
    			if(i != j)
    			{
    				spfa(0, i, j);//先跑一遍,判断是否能到达
    				if(dis[j] == 99999999999.00000)
    				{
    					ans[i][j] = -1.0000;//无法到达把答案记为-1				
    					continue;
    				}
    				double anss = 0.00000, low = 0.00000, mid, high = total * 1.00000;
    				while(high - low >= 0.00000)//浮点数二分要注意!
    				{
    					mid = (low + high) / 2.00000;
    					spfa(mid, i, j);
    					if(dis[j] > 0)
    					{
    						low = mid + 0.000001;//这里就不是+1了,尽量精确些!
    						anss = mid;
    					}
    					else
    					{
    						high = mid - 0.000001;
    					}
    				}
    				ans[i][j] = anss;
    			}
    		}
    	}
    	Q = read(); 
    	while(Q--)
    	{
    		int x, y, f = 0;
    		x = read();
    		y = read();
    		if(ans[x][y] == -1) puts("OMG!");
    		else printf("%.3lf\n", ans[x][y]);
    	}
            return 0;
    }
    
    
  • 相关阅读:
    Linux文件的复制、删除和移动命令
    Linux文件夹文件创建、删除
    Python 常用代码片段
    Chrome 插件 PageSpeed Insights
    VI打开和编辑多个文件的命令
    Linux case 及 函数位置参数
    C#编程利器之三:接口(Interface)
    C#编程利器之四:委托与事件(Delegate and event)
    解读设计模式简单工厂模式(SimpleFactory Pattern),你要什么我就给你什么
    C#编程利器之五:集合对象(Collections)
  • 原文地址:https://www.cnblogs.com/pjxpjx/p/16398447.html
Copyright © 2020-2023  润新知