51nod 1307 繩子與重物 二分+dfs / 並查集

題目鏈接:

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1307

題意:

題解:

方法一:
因爲所有繩子最終組成了1棵樹,所以我們可以通過一次DFS,來檢測是否有某根繩子下面綁了超過他所能負荷的重量。
具體方法:對每個節點,計算其子樹的重量和(包含自身的重量),如果大於能承受的最大重量,則繩子會斷,否則不會斷。
一次DFS時間複雜度是O(n)的。
既然一次判斷的複雜度是O(n)的,並且當繩子第一次斷掉後,繼續放重物,不會改變繩子斷掉的狀態(畢竟重物的重量都是正數,沒有負數),那麼我們可以用二分來做。
二分來求第一次斷掉的點,由於二分的複雜度是log(n),一次DFS判斷的複雜度是O(n),所以整個算法的複雜度是nlog(n)。
二分經常被用來求解一些具有單調性的問題,這裏的單調性就是斷掉以後,不會再次變成不斷的。

方法二:
我們在DFS的過程中,使用並查集,將子樹節點的Root指向當前節點,同時計算子樹的總重量。
假如當前節點的承重不足,那麼繩子會斷掉。所以我們需要去掉一些節點,來保證繩子不斷。根據題目要求,我們按照子樹節點的編號從高到低,逐個去掉這些重物,直到繩子不斷爲止。
那麼問題來了,並查集這個結構並不能解決按照編號從高到低去掉子樹的問題,如果自己維護其他的數據結構(比如堆),恐怕複雜度還要多一個Log。

所以我們跳出按照編號從高到低去掉子樹的思路,不如直接從編號N - 1到0去掉子樹,直到當前的繩子不斷爲止。 因爲編號小的斷了,後面再加的繩子也沒有用了,已經有繩子斷了就結束了。

代碼:

二分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e5+10;

struct node{
    int c,w,f;
}e[maxn];

vector<int> g[maxn];

bool book;

ll dfs(int u,int k){
    ll sum = e[u].w;
    if(u > k) return 0;
    for(auto v : g[u])
        sum += dfs(v,k);
    if(sum > e[u].c && u) book = false;
    return sum;
}

int main(){
    int n=read();
    for(int i=1; i<=n; i++){
        e[i].c=read(); e[i].w=read(); e[i].f=read(); e[i].f++;
        g[e[i].f].push_back(i);
    }

    int l=0,r=n,ans = 0;
    while(l<=r){
        int mid = (l+r)/2;
        book = true;
        dfs(0,mid);
        if(book) ans=mid,l=mid+1;
        else r=mid-1;
    }

    cout << ans << endl;

    return 0;
}

並查集:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e5+10;

struct node{
    ll c,w,f;
}e[maxn];

vector<int> g[maxn];
int fa[maxn];
ll ww[maxn];
int k;

int find(int x){
    return fa[x]==x ? x : fa[x]=find(fa[x]);
}

void update(int u){
    for(auto v : g[u]){
        e[u].w += e[v].w;
        fa[v] = u;
    }

    while(e[u].w > e[u].c){
        e[find(k)].w -= ww[k];
        k--;
    }
}

int main(){
    int n=read();
    for(int i=1; i<=n; i++){
        e[i].c=read(); e[i].w=read(); e[i].f=read(); e[i].f++;
        g[e[i].f].push_back(i); ww[i] = e[i].w;
        fa[i] = i;
    }

    k = n;
    for(int i=n; i>0; i--)
        update(i);

    cout << k << endl;

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章