51nod-1307 绳子与重物

33 篇文章 0 订阅
18 篇文章 0 订阅

题目来源:  Codility
基准时间限制:1 秒 空间限制:131072 KB 分值: 40  难度:4级算法题
 收藏
 关注
有N条绳子编号 0 至 N - 1,每条绳子后面栓了一个重物重量为Wi,绳子的最大负重为Ci。每条绳子或挂在别的绳子下或直接挂在钩子上(编号-1)。如果绳子下所有重物的重量大于绳子的最大负重就会断掉(等于不会断)。依次给出每条绳子的负重Ci、重物的重量Wi以及绳子会挂在之前的哪条绳子的下面,问最多挂多少个绳子而不会出现绳子断掉的情况。

例如下图:

5, 2, -1
3, 3, 0
6, 1, -1
3, 1, 0
3, 2, 3



挂到第4个时会有绳子断掉,所以输出3。


Input
第1行:1个数N,表示绳子的数量(1 <= N <= 50000)。
第2 - N + 1行:每行3个数,Ci, Wi, Pi,Ci表示最大负重,Wi表示重物的重量,Pi表示挂在哪个绳子上,如果直接挂在钩子上则Pi = -1(1 <= Ci <= 10^9,1 <= Wi <= 10^9,-1 <= Pi <= N - 2)。
Output
输出1个数,最多挂到第几个绳子,不会出现绳子断掉的情况。
Input示例
5
5 2 -1
3 3 0
6 1 -1
3 1 0
3 2 3
Output示例
3
思路:有三种思路,直接思维,二分,并查集

一,直接思维:

    从第一条绳子遍历,每次判断挂到第 i 条绳子时是否会断掉,判断是否断掉即,向上搜索它的连接绳子,判断所承载的重量是否大于它的最大承载即可。但当所有的绳子是连成一条直线时的最坏 时间复杂度是 O(n^2)

二, 二分: 

    可以对最多挂到的绳子数进行 二分查找。 而判断最多挂到第 k 条绳子时,可以从 第k条绳向前判断即可。这样的时间复杂度为 O(nlog(n))

三,并查集:

    对于所有的绳子从最后一条开始计算,当计算到 第 k 条绳子的承载重量 s[k]>最大承载重量 a[k].Max时,则依次将最后一条绳子去掉,直到 s[k]<=a[k].Max为止,只要计算到第一条绳子时就可得到最多挂到第几个绳子。


Code 1:

//直接思维-最坏时间复杂度 O(n^2) 
#include<iostream>
using namespace std;

const int MAX_N=50005;
struct node{
	int c;
	int w;
	int pre;
}a[MAX_N];
int n,ans;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int d,w,k=0;
	cin>>n;
	for(int i=0;i<n;++i)
		cin>>a[i].c>>a[i].w>>a[i].pre;
	for(int i=0;i<n;++i)
	{
		if(a[i].w>a[i].c){
			ans=i;	k=0;
			break;
		}
		d=a[i].pre;	w=a[i].w;
		while(d!=-1){
			a[d].w+=w;
			if(a[d].w>a[d].c){
				k=1;	ans=i;	break;
			}
			d=a[d].pre;
		}
		if(k)	break;
	}
	if(!k)	ans=n;
	cout<<ans<<endl;
	
	return 0;
}

Code 2:

// 二分 O(nlog(n))
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;

struct node{
	int per;
	int w;
	int Max;
};
const int MAX_N=50005;
int n;
LL s[MAX_N];	//s[i]:第i条绳子下所有重物的重量 
node a[MAX_N];
 
int main()
{
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=0;i<n;++i)
		cin>>a[i].Max>>a[i].w>>a[i].per;
	int l=0,r=n-1;
	while(l<=r){
		int h=(l+r)>>1;
		bool boo=true;
		memset(s,0,sizeof(s));
		for(int k=h;k>=0;--k)
		{
			s[k]+=a[k].w;
			if(s[k]>a[k].Max){
				boo=false;	break;
			}
			if(a[k].per!=-1)	s[a[k].per]+=s[k];
		}
		if(boo==true){
			l=h+1;
		}else	r=h-1;
	}
	cout<<r+1<<endl;
	return 0;
}

Code 3:

//并查集  O(n)
#include<iostream>
using namespace std;
typedef long long LL;

struct node{
	int per;
	int w;
	int Max;
};
const int MAX_N=50005;
int n;
int id[MAX_N];
LL s[MAX_N];
node a[MAX_N];

int Find(int k); 
int main()
{
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=0;i<n;++i)
	{
		cin>>a[i].Max>>a[i].w>>a[i].per;
		id[i]=i;	s[i]=a[i].w;
	}
	int ans=n-1;
	for(int i=n-1;i>=0;--i)
	{
		while(s[i]>a[i].Max){
			s[Find(ans)]-=a[ans].w;
			ans--;
		}
		s[a[i].per]+=s[i];
		id[i]=a[i].per;
	}
	cout<<ans+1<<endl;
	return 0;
}

int Find(int k)
{
	if(k!=id[k])	id[k]=Find(id[k]);
	return id[k];
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值