BZOJ 4349: 最小树形图(最小树形图->朱刘算法)

4349: 最小树形图

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 379   Solved: 167
[ Submit][ Status][ Discuss]

Description

小C现在正要攻打科学馆腹地------计算机第三机房。而信息组的同学们已经建好了一座座堡垒,准备迎战。小C作为一种高度智慧的可怕生物,早已对同学们的信息了如指掌。
攻打每一个人的堡垒需要一个代价,而且必须攻打若干次才能把镇守之人灭得灰飞烟灭。
当小C在绞尽脑汁想攻打方案时,突然从XXX的堡垒中滚出来一个纸条:一个惊人的秘密被小C发现了:原来各个堡垒之间会相互提供援助,但是当一个堡垒被攻打时,他对所援助的堡垒的援助就会停止,因为他自己已经自身难保了。也就是说,小C只要攻打某个堡垒一次之后,某些堡垒就只需要花更小的代价攻击了。
现在,要你求消灭全机房要用掉代价最小多少。

Input

第一行一个数N,(N<=50),表示机房修建的堡垒数。
接下来N行,每行两个数,第一个实数Ai表示攻打i号堡垒需要的代价Ai(0<Ai<=1000)。第二个数Bi(0<Bi<100)表示i号堡垒需要被攻打Bi次。
接下来一个数k,表示总共有k组依赖关系。
接下来k行每行三个数x,y,z(x,y,为整数,z为实数),表示攻打过一次x号堡垒之后,攻打y号堡垒就只要花z的代价,保证z比y原来的代价小。
不需要攻打的城堡不允许攻打。

Output

一行,一个实数表示消灭全机房要用的最小代价,保留两位小数。

Sample Input

3
10.00 1
1.80 1
2.50 2
2
1 3 2.00
3 2 1.50

Sample Output

15.50

HINT

Source


最小树形图模板题,下面介绍求最小树形图的步骤:(概念不再赘述,不懂自行百度)

1、找到除了root以为其他点的权值最小的入边。用In[i]记录

2、如果出现除了root以为存在其他孤立的点,则不存在最小树形图。

3、找到图中所有的环,并对环进行缩点,重新编号。
4、更新其他点到环上的点的距离,如:

环中的点有(Vk1,Vk2,… ,Vki)总共i个,用缩成的点叫Vk替代,则在压缩后的图中,其他所有不在环中点v到Vk的距离定义如下:gh[v][Vk]=min { gh[v][Vkj]-mincost[Vkj] } (1<=j<=i)

而Vk到v的距离为  gh[Vk][v]=min { gh[Vkj][v] }              (1<=j<=i)

 5、重复3,4知道没有环为止。

#include<stdio.h>  
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const double inf=1e10;
#define maxn 1010
#define maxm 40010
/*
* 最小树形图
* int型
* 复杂度O(NM)
* 点总0开始
*/
struct Edge  
{
	int u,v;
	double cost;
};   
Edge edge[maxm];
int pre[maxn],id[maxn],vis[maxn];
double in[maxn];
double zhuliu(int root,int n,int m,Edge edge[])
{
	int u,v,i;              
	double res=0;
	while(1)
	{
		for(i=0;i<n;i++)
			in[i]=inf;
		for(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(i=0;i<n;i++)      
			if(i!=root && in[i]==inf)   
				return -1;//不存在最小树形图*/   
		int tn=0;//记录有向环的数量     
		memset(id,-1,sizeof(id));
		memset(vis,-1,sizeof(vis));
		in[root]=0;
		for(i=0;i<n;i++)
		{
			res+=in[i];
			v=i;
			while(vis[v]!=i && id[v]==-1 && v!=root)
				vis[v]=i,v=pre[v];   
			if(v!=root && id[v]==-1)
			{   
				for(u=pre[v];u!=v;u=pre[u])
					id[u]=tn;   
				id[v]=tn++;   
			}              
		}             
		if(tn==0)//当前图中不再存在环了    
			break;         
		for(i=0;i<n;i++)
			if(id[i]==-1)
				id[i]=tn++;
		for(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;
}
int num[maxn],flag[maxn];
double a[maxn];
int main(void)
{
	double v,ans;
	int n,m,i,j,x,y,m1;
	while(scanf("%d",&n)!=EOF)
	{
		ans=0;int cnt=0;m=0;
		memset(flag,0,sizeof(flag));
		for(i=0;i<n;i++)
		{ 
			scanf("%lf%d",&v,&x);
			if(x==0)
				continue;
			flag[i]=++cnt;  
			edge[m].u=0;   
			edge[m].v=cnt;    
			edge[m++].cost=v;
			a[cnt]=v;num[cnt]=x-1;
		}
		n=cnt+1;   
		scanf("%d",&m1);
		for(i=1;i<=m1;i++)
		{
			scanf("%d%d%lf",&x,&y,&v);
			x--;y--;
			if(flag[x]==0 || flag[y]==0)
				continue;
			edge[m].u=flag[x];
			edge[m].v=flag[y];
			edge[m++].cost=v;  
			a[flag[y]]=min(a[flag[y]],v);
		}
		for(i=1;i<=cnt;i++)
			ans+=a[i]*num[i];
		printf("%.2f\n",ans+zhuliu(0,n,m,edge));
	}
	return 0;
}
/*
3
10.00 1
1.80 1
2.50 2
2
1 3 2.00
3 2 1.50
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值