题目大意
给出
每个绳子的承重Ci
小球的重量Wi
小球挂在那个小球上Pi
问最多挂多少个绳子而不会出现绳子断掉的情况。
思路
本文是看教程:O(n)可以解决的2道题
习题的第二题。
思路1 二分+DFS
按照我的想法,对绳子的列表进行二分 O(logn) ,每次判断绳子会不会断掉 O(n) ,使用DFS。最后的复杂度是 O(nlogn) 。
思路2 并查集
使用路径压缩之后并查集的平摊复杂度为
Alpha(n)
,这是一个增长非常缓慢的函数,在int64范围内的n,可以近似看做是常数。
使用并查集+DFS的方法复杂度可以看做是
O(Alpha(n)n)
要小于
O(nlogn)
。
使用并查集的时候要将维护建树的过程:
首先建树的时候维护元素node[i].W_S
如果以当前结点为根的子树的重量和大于了承重,就要删除最后一个元素(为什么是最后一个元素?因为重物是按照顺序添加的,可以从最后一个慢慢删,仅仅针对此题)。
代码1 二分+DFS
#include<iostream>
#include<stdio.h>
#include<vector>
using namespace std;
#define INF 1<<30
#define maxn 50005
int W[maxn];
int C[maxn];
int P[maxn];
int N;
int W_Sum[maxn];//绳子i现在的承重
vector<int> G[maxn];//与绳子i相连的点的集合
//这时候物体的结构已经被储存在了vector中
bool dfs(int u)
{
W_Sum[u] = W[u];
for(int i = 0; i<G[u].size(); i++)
{
if(!dfs(G[u][i])) return 0;
W_Sum[u] += W_Sum[G[u][i]];
}
return W_Sum[u] <= C[u];
}
bool Solve(int x)
{
for(int i = 0; i <= x; i++) G[i].clear();//将物体i挂在的绳子P[i]上的物体clear
for(int i = 1; i <= x; i++) G[P[i]].push_back(i); //物体i 挂在 绳子P[i]上
return dfs(0);
}
int main()
{
while(~scanf("%d",&N))
{
for(int i = 1; i <= N; i++)
{
scanf("%d%d%d",&C[i],&W[i],&P[i]),P[i]++;//分别是 绳子的最大负重 物体的重量 挂在哪个绳子上
}
C[0] = INF;//将根节点的最大承重设置为无限大
int l = 0;
int r = N;
while(l<r)
{
//printf("%d %d\n",l,r);
int mid = (l+r+1)/2;
if(Solve(mid))
{
l = mid;
}
else r = mid-1;
}
printf("%d\n",l);
}
}
代码2 并查集
#include<iostream>
#include<stdio.h>
#include<vector>
using namespace std;
#define INF 1<<30
#define maxn 50005
int N;
struct Node
{
int C,W,P,W_S;
} node[maxn];
int fa[maxn]; //fa[i]表示结点i的父节点
//查找结点i的源头
int Find_fa(int i)
{
if(fa[i]==i) return fa[i];
return fa[i] = Find_fa(fa[i]);
}
int main()
{
while(~scanf("%d",&N))
{
for(int i = 1; i <= N; i++)
{
scanf("%d%d%d",&node[i].C, &node[i].W, &node[i].P);//分别是 绳子的最大负重 物体的重量 挂在哪个绳子上
node[i].P++;
node[i].W_S = node[i].W;
//Init
fa[i] = i;
}
//
int ans = N;
for(int i = N; i ; i--)
{
while(node[i].W_S > node[i].C) //如果当前子树的重量 大于 结点的负重,就从最后一个添加的点开始删除
{
int k = Find_fa(ans);
node[k].W_S -= node[ans].W;//将node[ans]从子树中去掉
ans --;
}
node[node[i].P].W_S += node[i].W_S;//将以node[i]为结点的子树的总重量 加入到 以node[i]的父节点为子树的总重量 中
fa[i] = node[i].P;//建树
}
printf("%d\n",ans);
}
}
Hit
将物体由-1开始变成0开始