zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 need 条白色边的生成树。
    题目保证有解。

    input
    第一行V, E, need分别表示点数,边数和需要的白色边数。
    接下来 E 行,每行 s, t, c, col 表示这边的端点(点从0开始标号),边权,颜色(0白色,1黑色)。
    output
    一行表示所求生成树的边权和。
    V <= 50000, E<=100000, 所有数据边权为[1, 100]中的正整数。

    sample Input
    2 2 1
    0 1 1 1
    0 1 2 0
    sample output
    2

    @solution@

    dp 凸优化的起源……尽管这道题并不是 dp……
    事实上这个算法有三个名字:WQS 二分,dp 凸优化,带权二分。
    大家可以去看看这一位 dalao 的博客

    @part - 1@

    一个直观的想法是,我们先用 K 条白边生成一棵最小生成树,再把黑边加进去。
    然而好像是能被卡掉的。所以我们不能这么搞。

    那怎么去限制白边数量呢?我们给每条白边二分一个附加权值,再生成最小生成树,求出白边数量调整二分。
    可以从直观上感性理解:假如这个附加权值越大,则我们选取的白边会越少,反之会越大。因此附加权值与白边数量的关系是单调的,我们就可以玩二分了。

    最后从这个生成树中减去附加权值即可。

    @part - 2@

    然而这个算法……看起来并不是很严谨啊。
    会不会出现一种情况:二分的附加权值稍稍大一点白边数量就不够,二分的附加权值稍稍小一点白边数量又多出来了。总之就是二分不到题目说的 K 条。
    以及:这个附加权值可以为小数吗?我二分需要管它的精度问题吗?

    好的。我们慢慢来。
    先定义一个函数 (f(x)) 表示当我选取 (x) 条白边的时候,求得的最小生成树权值。
    经过 dalao 们的打表严格证明,我们可以得到 (f(x)) 是下凸函数。
    并且,我们也可以求出 (f(x)) 的最小值(原图中的最小生成树)。

    好,我们再来看我们刚刚的算法干了什么。我们二分了一个附加权值 (k),并把所有白边都加上了这个附加权值,得到一个新函数 (g(x) = f(x)+k*x)
    然后我们找到这个新函数的最小值(新图中的最小生成树)。求出它对应的横坐标(生成树所含的白边数量),并以此调整二分。

    上面我所提到的那位 dalao 提出了一种理解该算法的方法:我们对 (f(x))(g(x)) 分别求导,得到 (f'(x))(g'(x))
    因为 (f(x)) 是下凸的,所以 (f'(x)) 是单增的,同时 (f'(x)) 的零点对应着 (f(x)) 的最小值。
    可以得到 (g'(x) = f'(x) + k),即将 (f'(x)) 平移 (k) 个单位。这样就可以说明该算法的正确性了。
    同时,因为 (f(x)) 上的点都是整数,所以 (f'(x)) 上的点也应该都是整数,所以我们不需要进行小数二分。

    @accepted code@

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int MAXN = 50000;
    struct edge{
    	int u, v, w;
    	edge(int _u=0, int _v=0, int _w=0):u(_u), v(_v), w(_w){}
    };
    bool operator < (edge a, edge b) {
    	return a.w < b.w;
    } 
    vector<edge>edges[2];
    void init() {
    	for(int i=0;i<2;i++)
    		edges[i].clear();
    }
    int fa[MAXN + 5];
    int find(int x) {
    	return (fa[x] == x) ? x : fa[x] = find(fa[x]);
    }
    int N, M, K, ans, cnt, p, q;
    bool check(int x) {
    	ans = cnt = p = q = 0;
    	for(int i=1;i<=N;i++)
    		fa[i] = i;
    	while( p < edges[0].size() && q < edges[1].size() ) {
    		if( find(edges[0][p].u) == find(edges[0][p].v) )
    			p++;
    		else if( find(edges[1][q].u) == find(edges[1][q].v) )
    			q++;
    		else {
    			if( edges[0][p].w + x < edges[1][q].w ) {
    				ans += edges[0][p].w + x;
    				fa[find(edges[0][p].u)] = find(edges[0][p].v);
    				cnt++; p++;
    			}
    			else {
    				ans += edges[1][q].w;
    				fa[find(edges[1][q].u)] = find(edges[1][q].v);
    				q++;
    			}
    		}
    	}
    	while( p < edges[0].size() ) {
    		if( find(edges[0][p].u) == find(edges[0][p].v) )
    			p++;
    		else {
    			ans += edges[0][p].w + x;
    			fa[find(edges[0][p].u)] = find(edges[0][p].v);
    			cnt++; p++;
    		}
    	}
    	while( q < edges[1].size() ) {
    		if( find(edges[1][q].u) == find(edges[1][q].v) )
    			q++;
    		else {
    			ans += edges[1][q].w;
    			fa[find(edges[1][q].u)] = find(edges[1][q].v);
    			q++;
    		}
    	}
    	return cnt <= K;
    }
    int main() {
    	init();
    	scanf("%d%d%d", &N, &M, &K);
    	for(int i=1;i<=M;i++) {
    		int a, b, c, x;
    		scanf("%d%d%d%d", &a, &b, &c, &x);
    		edges[x].push_back(edge(a + 1, b + 1, c));
    	}
    	for(int i=0;i<2;i++)
    		sort(edges[i].begin(), edges[i].end());
    	int le = -100, ri = 100;
    	while( le < ri ) {
    		int mid = (le + ri) >> 1;
    		if( check(mid) ) ri = mid;
    		else le = mid + 1;
    	}
    	check(le);
    	printf("%d
    ", ans - K*le);
    }
    

    @details@

    事实证明,这道题 bzoj 的数据非常的水。
    我带权二分的时候连那个二分出来的附加权值都没有加到边权里去。
    然后在 bzoj 上它竟然 AC 了?

    建议大家如果要检测自己代码的正确性,去 hdu4253 (需要写多组数据)。
    否则你连自己代码有错都不知道,一懵一懵地好像自己就 AC 了。

  • 相关阅读:
    系统设计与架构笔记:对我新公司网站的技术架构初解
    与国内某知名互联网公司交流后的心得
    系统架构:Web应用架构的新趋势---前端和后端分离的一点想法
    为什么做java的web开发我们会使用struts2,springMVC和spring这样的框架?
    我设计的网站的分布式架构
    Python Day 46 前端 、HTML5介绍、HTML标签、标签的嵌套规则、CSS3介绍、CSS代码中书写位置(重点)、CSS基础选择器、
    Python Day 45 手撸ORM框架
    Python Day 44 Mysql数据库备份及优化(六)
    Python Day 43 Mysql基础语法(五)sqlalchemy、创建表、增删改查、高级查询操作、正向反向查询
    Python Day 42 Mysql基础语法(四)、存储引擎、索引、慢日志查询、普通日志记录(general log)、权限管理、explain工具
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10214533.html
Copyright © 2011-2022 走看看