[HDOJ 4966] GGS-DDU [最小树形图]

69 篇文章 0 订阅
1 篇文章 0 订阅

有一个人,最开始所有技能等级都是0。有m种药,对于每种药,如果他的c技能等级达到了l1,就可以用e的费用嗑药,使d技能的等级达到l2。当然如果d技能的等级已经超过了l2,那他的技能等级也不会下降,而是会维持原来的等级。

问把所有技能等级都提升到满级,最少需要多少花费。

所以每个技能从高等级向低等级连边,初始点向所有的0技能连边,这些边的费用都是0。然后对于每种药,从c技能的l1等级向d技能的l2等级连边,费用为该药的费用。求最小树形图就行。

最小树形图就是有向图的最小生成树,在给定一个点作为根的前提下,要求所有的边都是从根向叶子方向连的。用一种叫朱刘算法的方法做..复杂度为n*m..据说很暴力...

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

int map[600][600];
using namespace std;
int n,m;
int TOT;
int INF = 0x1f1f1f1f;

void addedge(int a ,int b,int c)
{
	//printf("%d %d %d\n",a,b,c);
	map[a][b]=min(map[a][b],c);
}

int flag[600],father[600];
void merg(int k,int r)
{
	for(int i=0;i<n;i++)
	{
		map[k][i]=min(map[k][i],map[r][i]);
		map[i][k]=min(map[i][k],map[i][r]);
	}
	for(int i=0;i<n;i++)map[r][i]=map[i][r]=INF;
}

bool dfs(int k,int r)
{
	flag[k]=r;
	int len=INF;
	int t=-1;
	for(int i=0;i<n;i++)
	{
		if(len>map[i][k])
			len=map[i][k],t=i;
	}
	if(t==-1)return false;
	TOT+=len;
	father[k]=t;
	if(flag[t]!=-1 && flag[t]<r)return true;
	else if(flag[t]==r)
	{
		int d=t;
		do
		{
			for(int i=0;i<n;i++)
			{
				if(i==father[d])continue;
				if(map[i][d]!=INF)map[i][d]-=map[father[d]][d];
			}
			d=father[d];
		}while(d!=t);
		d=t;
		do
		{
			t=father[t];
			merg(d,t);
		}while(t!=k);
		map[d][d]=INF;
		return dfs(d,r);
	}
	else return dfs(t,r);
}

bool exist(int root)
{
	memset(flag,-1,sizeof(flag));
	flag[0]=0;
	for(int i=1;i<n;i++)
	{
		if(flag[i]!=-1)continue;
		if(!dfs(i,i))return false;
	}
	return true;
}

int init(int n,int m) {
	static int a[50];
	static int st[51];
	//addedge(a,b,c);
	int newn=0,i,j;
	st[0]=1;
	for (i=0;i<n;i++) {
		scanf("%d",&a[i]);
		st[i+1]=a[i]+1+st[i];
		addedge(0,st[i],0);
		for (j=0;j<a[i];j++) {
			addedge(st[i]+j+1,st[i]+j,0);
		}
	}
	newn=st[n];
	for (i=0;i<m;i++) {
		int c,l1,d,l2,e;
		scanf("%d%d%d%d%d",&c,&l1,&d,&l2,&e);
		addedge(st[c-1]+l1,st[d-1]+l2,e);
	}
	return newn;
}

int main()
{
	while(scanf("%d%d",&n,&m)!=EOF )
	{
		if(n==0 && m==0)break;
		memset(map,0x1f,sizeof(map));
		n=init(n,m);
		TOT=0;
		if(exist(0))printf("%d\n",TOT);
		else printf("-1\n");
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值