zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    有n个城市(编号从0..n-1),m条公路(双向的),从中选择n-1条边,使得任意的两个城市能够连通,一条边需要的c的费用和t的时间,定义一个方案的权值 v = (n-1条边的费用和)*(n-1条边的时间和),你的任务是求一个方案使得v最小。

    Input
    第一行两个整数n,m,接下来每行四个整数a,b,c,t,表示有一条公路从城市a到城市b需要t时间和费用c
    Output
    仅一行两个整数sumc,sumt,(sumc表示使得v最小时的费用和,sumc表示最小的时间和) 如果存在多个解使得sumc*sumt相等,输出sumc最小的

    Sample Input
    5 7
    0 1 161 79
    0 2 161 15
    0 3 13 153
    1 4 142 183
    2 4 236 80
    3 4 40 241
    2 1 65 92
    Sample Output
    279 501

    HINT
    【数据规模】
    1<=N<=200, 1<=m<=10000, 0<=a,b<=n-1, 0<=t,c<=255。
    有5%的数据m=n-1
    有40%的数据有t=c
    对于100%的数据如上所述

    @solution@

    非常经典的题.jpg。

    假如将 (sumc, sumt) 看成一个坐标,那么一个可行生成树方案对应了坐标系中的一个点。

    可以发现:只有下凸包上的点才可能成为答案。
    如果不在下凸包上,那么可以作原点到该点的直线,它与凸包的交点显然更优。
    同时,一条线段肯定取两个端点得到的乘积最小(写出表达式发现是二次函数)。

    因为三点共线肯定不优,所以凸包上的斜率互不相同且递减。因此,凸包上的点最多只有 (O(sqrt{N*max{c, t}})) 个点。
    我们只需要尝试找出凸包上的点并更新答案即可。

    考虑两个必定在凸包内的点 A(minx, y) 与 B(x, miny)(不可能找到更大的凸包严格包含这两个点)。
    如果忽视掉下凸包中斜率为正的部分(这部分肯定不优),这两个点就是凸包上的点横纵坐标的边界。

    我们根据直线 AB,找该直线在凸包上对应的切线,就可以又找到一个新的凸包上的点。
    其实就是距离 AB 最远的点 C。距离最远可以转成 ABC 的面积最大,然后可以写出叉积。
    根据叉积式子再做一遍最大生成树(注意是最大)就可以找到 C 了。

    然后分治 AC, CB 即可。
    时间复杂度的一个上界为 (O(sqrt{N*max{c, t}}*Mlog M))

    @accepted code@

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int MAXN = 200;
    const int MAXM = 10000;
    const int INF = int(1E9);
    typedef long long ll;
    int n, m;
    struct point{
    	int c, t; ll k;
    	point(int _c=0, int _t=0) : c(_c), t(_t), k(1LL*_c*_t) {}
    	friend point operator + (point a, point b) {
    		return point(a.c + b.c, a.t + b.t);
    	}
    }ans(INF, INF);
    void update(point p) {
    	if( p.k < ans.k || (p.k == ans.k && p.c < ans.c) )
    		ans = p;
    }
    int a, b;
    struct edge{
    	int u, v; point p;
    	ll get() {return 1LL*a*p.c + 1LL*b*p.t;}
    	friend bool operator < (edge a, edge b) {
    		return a.get() < b.get();
    	}
    }e[MAXM + 5];
    int fa[MAXN + 5];
    int find(int x) {
    	return fa[x] = (fa[x] == x ? x : find(fa[x]));
    }
    point get() {
    	for(int i=1;i<=n;i++) fa[i] = i;
    	sort(e + 1, e + m + 1);
    	point ret(0, 0);
    	for(int i=1;i<=m;i++) {
    		int fu = find(e[i].u), fv = find(e[i].v);
    		if( fu != fv ) {
    			ret = ret + e[i].p;
    			fa[fu] = fv;
    		}
    	}
    	return ret;
    }
    void solve(point A, point B) {
    	a = (A.t - B.t), b = (B.c - A.c);
    	point C = get();
    	if( a*C.c + b*C.t + (B.t - A.t)*B.c + (A.c - B.c)*B.t >= 0 )
    		return ;
    	update(C), solve(A, C), solve(C, B);
    }
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i=1;i<=m;i++) {
    		scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].p.c, &e[i].p.t);
    		e[i].u++, e[i].v++;
    	}
    	a = 1, b = 0; point A = get(); update(A);
    	a = 0, b = 1; point B = get(); update(B);
    	solve(A, B);
    	printf("%d %d
    ", ans.c, ans.t);
    }
    

    @details@

    其实这个模型之所以经典,是因为它的可迁移性很强。
    比如给你整一个下一次最小乘积最短路,最小乘积最小割之类的。

    另外,我们 01 分数规划 + 最小生成树,尽管平时用的是二分,其实也可以采用几何方法来做。
    但是复杂度就很玄妙了。

  • 相关阅读:
    【阿里云IoT+YF3300】6.物联网设备报警配置
    【阿里云IoT+YF3300】5. Alink物模型之服务下发
    【阿里云IoT+YF3300】4.Alink物模型之事件触发
    【阿里云IoT+YF3300】3. Alink物模型之属性上传和下发
    腾讯物联TencentOS tiny上云初探
    【阿里云IoT+YF3300】2.阿里云IoT云端通信Alink协议介绍
    【阿里云IoT+YF3300】1.时代大背景下的阿里云IoT物联网的现状和未来
    阿里云的物联网之路
    阿里云智能接入网关体验
    imx:MfgTool
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11788817.html
Copyright © 2011-2022 走看看