51nod 1307 绳子与重物【并查集】【搜索】【二分法】

来源:互联网 发布:手机电子秤软件下载 编辑:程序博客网 时间:2024/04/29 14:43

  • 题目大意
  • 思路
    • 思路1 二分DFS
    • 思路2 并查集
  • 代码1 二分DFS
  • 代码2 并查集
  • Hit

题目大意

传送门

给出
每个绳子的承重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 50005int 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 50005int 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开始

原创粉丝点击