HDU4966 GGS-DDU(最小树形图)

之前几天想着补些算法的知识,学了一下最小树形图的朱刘算法,不是特别理解,备了份模板以备不时之需,想不到多校冷不丁的出了个最小树形图,没看出来只能表示对算法不太理解吧,用模板写了一下,然后就过了。- -0

之前听到是最小树形图的时候觉得恍然大悟,非常裸,但是后来想想也不是特别裸,其实关键就是要想清楚要加回流的边,贴一份代码吧- -0

#pragma warning(disable:4996)
#include<cstdio>
#include<set>
#include<cstring>
#include<iostream>
#include<stdlib.h>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include<cmath>
#include<functional>
#include<string>
using namespace std;

#define maxn 550

int n, m;
int a[55];

struct Edge{
	int u, v, w;
	Edge(int ui, int vi, int wi) :u(ui), v(vi), w(wi){}
	Edge(){}
};

vector<Edge> E;
vector<int> vid[55];

int in[maxn]; // minimum pre edge weight
int pre[maxn]; // pre vertex
int vis[maxn]; // vis array
int id[maxn]; // mark down the id
int nv; // nv is the number of vertex after shrinking

int directed_mst(int root,int vertex_num)
{
	int ret = 0; int nv = vertex_num;
	while (1){
		for (int i = 0; i < nv; ++i) in[i] = 1e9;
		for (int i = 0; i < E.size(); ++i){
			int u = E[i].u, v = E[i].v;
			if (E[i].w < in[v] && u != v){
				in[v] = E[i].w;
				pre[v] = u;
			}
		}
		for (int i = 0; i < nv; ++i){
			if (i == root) continue;
			if (in[i]>1e8) return -1;
		}
		int cnt = 0;
		memset(id, -1, sizeof(id));
		memset(vis, -1, sizeof(vis));
		in[root] = 0;

		for (int i = 0; i < nv; ++i){
			ret += in[i];
			int v = i;

			while (vis[v] != i&&id[v] == -1 && v != root){
				vis[v] = i;
				v = pre[v];
			}
			// v!=root means we find a circle,id[v]==-1 guarantee that it‘s not shrinked.
			if (v != root&&id[v] == -1){
				for (int u = pre[v]; u != v; u = pre[u]){
					id[u] = cnt;
				}
				id[v] = cnt++;
			}
		}
		if (cnt == 0) break;
		for (int i = 0; i < nv; ++i){
			if (id[i] == -1) id[i] = cnt++;
		}
		// change the cost of edge for each (u,v,w)->(u,v,w-in[v])
		for (int i = 0; i < E.size(); ++i){
			int v = E[i].v;
			E[i].u = id[E[i].u];
			E[i].v = id[E[i].v];
			if (E[i].u != E[i].v) E[i].w -= in[v];
		}
		// mark down the new root
		root = id[root];
		// mark down the new vertex number
		nv = cnt;
	}
	return ret;
}

int main()
{
	while (cin >> n >> m){
		if (n == 0 && m == 0) break;
		int tot = 0;
		for (int i = 1; i <= n; ++i) {
			vid[i].clear();
			scanf("%d", a + i);
			for (int j = 0; j <= a[i]; ++j){
				vid[i].push_back(++tot);
			}
		}
		++tot;
		E.clear();
		for (int i = 1; i <= n; ++i){
			for (int j = 0; j < vid[i].size(); ++j){
				for (int k = j + 1; k < vid[i].size(); ++k){
					E.push_back(Edge(vid[i][k], vid[i][j], 0));
				}
			}
		}
		int ci, l1, di, l2, wi;
		for (int i = 0; i < m; ++i){
			scanf("%d%d%d%d%d", &ci, &l1, &di, &l2, &wi);
			E.push_back(Edge(vid[ci][l1], vid[di][l2], wi));
		}
		for (int i = 1; i <= n; ++i){
			E.push_back(Edge(0, vid[i][0], 0));
		}
		int ans = directed_mst(0,tot);
		printf("%d\n", ans);
	}
	return 0;
}

HDU4966 GGS-DDU(最小树形图),布布扣,bubuko.com

时间: 2024-10-13 12:15:29

HDU4966 GGS-DDU(最小树形图)的相关文章

hdu4966 最小树形图 /刘朱算法

建图(每次lev[i+1]到lev[i]连边,权是0,图中原来有边.root到lev0的有边.到达最高的点相当于必需到达所有点])之后,便是最小树形图(有向图,包括指定的根在内的最小生成树(从根出发)). 刘朱算法很好理解: 选取每个点入度最小的边加入边集. 缩点,改权. 取最小边集. 这考的比较少,就直接用模板了..之前先自己dfs一遍,判断有无解. 模板用的是邻接矩阵的.之前必需对点1--n重新编号. #include<iostream> #include<cstdio> #i

&ldquo;Hdu4966&rdquo; GGS-DDU - 最小树形图/朱刘算法

原文引用https://www.dazhuanlan.com/2019/08/26/5d6302f8c23db/ 题目链接:传送门 Description 有一个人,想学习N个科目,每个科目都有相应的层次 有M个课程,M个课程的要求是,你的第c个科目的层次要达到l1,才可以参加,参加完这个课程后,你需要缴费money,但你的第d个科目的层次会达到l2 问如何花最少的钱,使得每个科目的层次都达到最高 Solution 每个科目的每个层次都看成一个点,每个科目的层次0和一个根连边,费用为0:每个科目

bzoj4349: 最小树形图

最小树形图模板题…… 这种\(O(nm)\)的东西真的能考到么…… #include <bits/stdc++.h> #define N 60 #define INF 1000000000 using namespace std; int n, m, nn; double ai[N], an[N], ci[2][N][N], ans; int bc[N]; int ini[N], vis[N], inc[N], inl[N]; int dfn; int dfs(int t) { vis[t]

Directed_MST 最小树形图

List Directed_MST 最小树形图 List Knowledge 模板 Practice 参考资料 Knowledge 求一个图中遍历到每个点的方案中的最小边权和,显然n-1条边,即一颗树即可. 最小生成树?当然这里不是的,这里的最小树形图算法是针对有向图的. 最小树形图的第一个算法是1965年朱永津和刘振宏提出的复杂度为O(VE)的算法.简称朱刘算法. 1986年, Gabow, Galil, Spencer和Tarjan提出了一个复杂度更好的实现,其时间复杂度为O(E+VlogV

hdu2121+不定根最小树形图

算和定根最小树形图相同. 我们只需:设一个权值sumw=所有边之和+1,类似于网络流,向图中加入一个超级源点,把这个点作为虚根.虚根到其他所有点之间连上一条边,边权值为sumw. 求出的值减去sumw即为最小树形图的权值. 当然,返回-1则无解.此外,当求出的值>=2*sumw,也是无解的. 1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 struct node 5 { 6 int u,v; 7

HDOJ 2121 Ice_cream’s world II 最小树形图无根树

朱刘算法 最小树形图无根树: 建立一个虚拟的根节点,向所有节点连边,权值为其他所有边的权值和+1 在求最小树形图的时候,记录和虚拟的根相连的是哪个节点 在这题中,边是从小往大加的所以直接记录的是相连的是第几号边.... Ice_cream's world II Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3442    Accept

最小树形图【模板】

基于贪心和缩点的思想. 假设根的顶点是V0. (1)除了根结点外,所有的点Vi,找到以Vi为终点的最短的边,加入集合中 (pre[v]存放的是终点v的起点,In[v]存放终点为v的最短的边) (2)检查集合中有没有有向环和收缩点.若没有有向环和收缩点,结束计算:若没有有向环.但含收缩边,则跳至步骤(4):若含有有向环,则跳至步骤(3).Ps:如果出现重边,将忽略权值较高的 (3)含有有向环,则收缩有向环(缩点),把有向环收缩为一个点,其有向环内的边被收缩掉,而环外的边被保 留,构建新图,重复步骤

HDU 2121 Ice_cream’s world II (不定根最小树形图)

题目地址:HDU 2121 这题没有给定根.最容易想到的当然是暴力,枚举所有的根,但是TLE是显然的..为了处理不定根的情况,可以虚拟一个根,然后用这个根去跟所有的点连边,权值为其他所有权值的和+1,目的是防止成为最小树形图的一条边.然后跑出最小树形图后,那么这个虚拟根肯定跟一个实际根相连,这时候根就找到了,然后再在最终的总花费中减去虚拟的那条边的权值就可以了. 代码如下: #include <iostream> #include <string.h> #include <m

HDU2121 Ice_cream’s world II【最小树形图】【不定根】

Ice_cream's world II Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3005    Accepted Submission(s): 704 Problem Description After awarded lands to ACMers, the queen want to choose a city be he