HDU 4966 GGS-DDU 最小树形图

题意:

给定n个技能,m个限制

下面是每个技能满级的级数

开始每个技能都是0级。

m个限制

(c,l1) (d,l2) cost

若c技能已经>=l1级,那么把点亮d技能 从0级一路点到l2级的花费是cost

。。他说的好有道理,我竟无言以对 _(:зゝ∠)_

最小树形图,用0做根,触发每个技能的0级花费是0

若已经点亮技能的x级,则点亮该技能的x-1级花费就是0

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;
/*
* 最小树形图
* 复杂度O(NM)
* 点下标[0,n-1] 边下标[0,m-1]
* 有向边表示:u->v 花费为cost
* 返回最小树形图的边权和,-1表示不存在最小树形图
*/
const int INF = 100000000;
const int MAXN = 1010; //点数
const int MAXM = 1010000;//边数
#define ll int
struct Edge{
	int u,v;
	ll cost;
}edge[MAXM];
int pre[MAXN],id[MAXN],visit[MAXN],edgenum;
void add(int u, int v, ll cost){
	Edge E = {u, v, cost}; edge[edgenum++] = E;
}

ll in[MAXN];
ll zhuliu(int root,int n,int m,Edge edge[])//树根(注意是有向树,树根不能任意) 点数 边数 edge
{
	int u,v;
	ll res=0;
	while(1)
	{
		for(int i = 0;i < n;i++)
			in[i] = INF;
		for(int i = 0;i < m;i++)
			if(edge[i].u != edge[i].v && edge[i].cost < in[edge[i].v])
			{
				pre[edge[i].v] = edge[i].u;
				in[edge[i].v] = edge[i].cost;
			}
			for(int i = 0;i < n;i++)
				if(i != root && in[i] == INF)
					return -1;//不存在最小树形图
			int tn = 0;
			memset(id,-1,sizeof(id));
			memset(visit,-1,sizeof(visit));
			in[root] = 0;
			for(int i = 0;i < n;i++)
			{
				res += in[i];
				v = i;
				while( visit[v] != i && id[v] == -1 && v != root)
				{
					visit[v] = i;
					v = pre[v];
				}
				if( v != root && id[v] == -1 )
				{
					for(int u = pre[v]; u != v ;u = pre[u])
						id[u] = tn;
					id[v] = tn++;
				}
			}
			if(tn == 0)break;//没有有向环
			for(int i = 0;i < n;i++)
				if(id[i] == -1)
					id[i] = tn++;
			for(int i = 0;i < m;)
			{
				v = edge[i].v;
				edge[i].u = id[edge[i].u];
				edge[i].v = id[edge[i].v];
				if(edge[i].u != edge[i].v)
					edge[i++].cost -= in[v];
				else
					swap(edge[i],edge[--m]);
			}
			n = tn;
			root = id[root];
	}
	return res; //-1为不存在最小树形图
}
void init(){
	edgenum = 0;
}
#define N 55
int n, m, a[N], sum[N];
int Hash(int i, int j){
	return sum[i-1]+j;
}
int main(){
	int i, j, c, l1, d, l2, cost;
	while(scanf("%d %d",&n,&m), n+m){
		init();
		sum[0] = 0;
		for(i = 1; i <= n; i++)scanf("%d",&a[i]), a[i]++, sum[i] = sum[i-1]+a[i];
		for(i = 1; i <= n; i++)
			add(0, Hash(i,1), 0);
		for(i = 1; i <= n; i++)
			for(j = 2; j <= a[i]; j++)
				add(Hash(i,j), Hash(i,j-1), 0);
		while(m--) {
			scanf("%d %d %d %d %d",&c, &l1, &d, &l2, &cost);
			l1++; l2++;
			add(Hash(c,l1), Hash(d,l2), cost);
		}
		printf("%d\n", zhuliu(0, sum[n]+1, edgenum, edge));
	}
	return 0;
}

HDU 4966 GGS-DDU 最小树形图,布布扣,bubuko.com

时间: 2024-10-12 06:01:30

HDU 4966 GGS-DDU 最小树形图的相关文章

HDU 4966 GGS-DDU(最小树形图)

n个技能,每个技能有0-a[i]的等级,m个课程,每个课程需要前置技能c[i]至少达到lv1[i]等级,效果是技能d[i]达到lv2[i]等级,花费w[i]. 输出最小花费使得全技能满级(初始全技能0等级) n<=50,Σa[i]<=500,m<=2000 点<=551,边<=2000+50+Σ((a[i]+1)*a[i]/2) Σw[i]<=2000*1000<0x3f3f3f3f 比赛时候完全不在状态,什么题都想不到,坑队友了... 最小树形图-做过tarja

hdu 4009 Transfer water(最小树形图:有向图的最小生成树模板)

题目: 链接:点击打开链接 题意: 有n个村庄,要求使得每个村庄都能得到水的最小费用.每个村庄可以通过挖井或从其他村庄修水路获得水.挖井的费用是房子的高度乘以X,修水道的费用和有向图边的起点和终点的高度有关. 思路: 代码: #include <iostream> #include <cstdio> #include <cmath> #include <cstring> using namespace std; #define inf 0x3f3f3f3f

HDU 4009 Transfer water 最小树形图

分析:建一个远点,往每个点连建井的价值(单向边),其它输水线按照题意建单向边 然后以源点为根的权值最小的有向树就是答案,套最小树形图模板 #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <stack> #include <

hdu 4966 GGS-DDU (最小树形图)

比较好的讲解:http://blog.csdn.net/wsniyufang/article/details/6747392 view code//首先为除根之外的每个点选定一条入边,这条入边一定要是所有入边中最小的. //现在所有的最小 入边都选择出来了,如果这个入边集不存在有向环的话,我们 //可以证明这个集合就是该图的最小树形图.这个证明并不是很难.如果存在有向 //环的话,我们就要将这 个有向环所称一个人工顶点,同时改变图中边的权.假 //设某点u在该环上,并设这个环中指向u的边权是in

hdu 4966 最小树形图

将每门课等级拆成0,1,2,3...a[i]个点,对每个等级大于0的点向它低一级连边,权值为0[意思是,若修了level k,则level(0~k)都当做修了] 将输入的边建边,权值为money[i]. 建立根节点,向每个level 0的点连边,权值为0[因为初始level 0的都修了] 由于题目要求每门课都必须达到最大level,也就是对应图中根节点能到达所有点,问题就变成了求无向图的最小生成树. #include<iostream> #include<cstdio> #incl

HDU 4966 GGS-DDU (最小树形图-朱刘算法)

题目地址:HDU 4966 刚开始没看清总级别只有500这一条件,看成了每一个都是500..然后建图思路就想歪了.....后来才发现是总共只有500..那么建图就很简单了..把每个科目的每个等级都设为一个点,把所有的0等级设为同一个树根.然后把所有科目的高等级向低等级连一条权值为0的有向边,第一个作用是保证最后的最小树形图是所有点都可达,第二个作用是保证每节课的的所需等级,只要达到高等级,那么使低等级也符合.然后再对每节课连边即可. 代码如下: #include <iostream> #inc

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

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

[tarjan+最小树形图] hdu 3072 Intelligence System

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3072 Intelligence System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1479    Accepted Submission(s): 653 Problem Description After a day, ALP

hdu 3072 有向图缩点成最小树形图计算最小权

题意,从0点出发,遍历所有点,遍历边时候要付出代价,在一个SCC中的边不要付费.求最小费用. 有向图缩点(无需建立新图,,n<=50000,建则超时),遍历边,若不在一个SCC中,用一个数组更新记录最小到达该连通分量的最小边权即可...边聊天,边1A,哈哈... #include<iostream> #include<stack> #include<queue> #include<cstdio> #include<cstring> usin