1. 程式人生 > >北京師範大學第十六屆程序設計競賽決賽 F 湯圓防漏理論

北京師範大學第十六屆程序設計競賽決賽 F 湯圓防漏理論

lap 示例 names col copy AC www. 總結 tor

鏈接:https://www.nowcoder.com/acm/contest/117/F
來源:牛客網

湯圓防漏理論 時間限制:C/C++ 1秒,其他語言2秒
空間限制:C/C++ 32768K,其他語言65536K
64bit IO Format: %lld

題目描述

ghc很喜歡吃湯圓,但是湯圓很容易被粘(zhān)漏。

根據多年吃湯圓經驗,ghc總結出了一套湯圓防漏理論:

互相接觸的湯圓容易粘(zhān)在一起,並且接觸面積不同,粘(zhān)在一起的粘(nián)度也不同。

當ghc要夾起一個湯圓時,這個湯圓和現在碗裏與這個湯圓接觸的所有湯圓之間的粘(nián)度的和,如果大於湯圓的硬度,這個湯圓就會被粘(zhān)漏。

今天ghc又要煮湯圓啦,今天要煮n個湯圓,並且擺盤的方法已經設計好:

湯圓按照技術分享圖片

編號,有m對湯圓互相接觸,用xi, yi, zi表示編號為xi和yi的兩個湯圓互相接觸,粘(nián)度為zi

湯圓當然是越軟越好吃,但是ghc的廚藝只允許把所有湯圓煮成同樣的硬度。那麽,湯圓的硬度最小可以是多少,可以滿足吃的過程中,存在一種夾湯圓的順序,使得沒有湯圓會被粘(zhān)漏呢?

註意:

不考慮湯圓的重力作用;

不能同時夾多個湯圓;

吃完湯圓一定要喝點湯。

輸入描述:

第一行是一個正整數T(≤ 5),表示測試數據的組數,

對於每組測試數據,

第一行是兩個整數n,m(1≤ n,m≤ 100000),

接下來m行,每行包含三個整數xi, yi, zi(1≤ xi, yi ≤ n, xi

≠ yi, 1 ≤ zi ≤ 1000000),

同一對湯圓不會出現兩次。

輸出描述:

對於每組測試數據,輸出一行,包含一個整數,表示湯圓硬度的最小值。
示例1

輸入

復制
1
4 6
1 2 2
1 3 2
1 4 2
2 3 3
2 4 3
3 4 5

輸出

復制
6

法一:直接算

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

using LL = long long;
using P = pair<LL, int>;

LL cnt[N];
int n, m;

set<P> edge[N]; priority_queue<P, vector<P>, greater<P> > Q; void Work() { LL ans = 0; for(int i = 1; i <= n; i++) { Q.push({cnt[i], i}); } while(!Q.empty()) { auto tmp = Q.top(); Q.pop(); if(tmp.first != cnt[tmp.second]) continue; int u = tmp.second; ans = max(ans, tmp.first); for(auto p : edge[u]) { int v = p.second; cnt[v] -= p.first; edge[v].erase({p.first, u}); Q.push({cnt[v], v}); } } cout << ans << endl; } int main() { int T; cin >> T; while(T--) { cin >> n >> m; for(int i = 1; i <= n; i++) { edge[i].clear(); cnt[i] = 0; } int u, v, w; for(int i = 1; i <= m; i++) { cin >> u >> v >> w; cnt[u] += w; cnt[v] += w; edge[u].insert({w, v}); edge[v].insert({w, u}); } Work(); } }

技術分享圖片
#include<cstdio>
#include<cmath>
#include<set>
#include<queue>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
#define ll      long long
#define N       100000
#define mod     1000000007
#define pa      pair<ll,ll>
vector<pa>g[N+5];
set<pa>p;
ll sum[N+5];        //表示編號為i的人的粘稠度綜合
bool vis[N+5];      //去重
int main()
{
    int i,j,t,q,n,m,a,b,w,x;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)        //p不需要清了,本來就是空的
        {
            g[i].clear();
            sum[i]=0;
            vis[i]=false;
        }
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&w);
            g[a].push_back(pa(b,w));
            g[b].push_back(pa(a,w));    //將a,b連接起來
            sum[a]+=w;
            sum[b]+=w;
        }
        for(i=1;i<=n;i++)
            p.insert(pa(sum[i],i));     //將數據輸入到set
        ll maxd=0; 
        while(!p.empty())           //貪心
        {
            set<pa>::iterator it=p.begin();
            pa now= *it;    //now.first表示他的粘稠度,now,second表士坐標
            p.erase(*it);
            maxd=max(now.first,maxd);
        //  printf("%lld  now.first=%lld\n",maxd,now.first);
            x=now.second;
            vis[x]=true;            //標記   
            //第一步,刪除權值的邊
            for(i=0;i<g[x].size();i++)
            {
                pa l=g[x][i];           //為了理解,再寫下,l.first是x對應的邊
                if(vis[l.first])
                    continue;
                p.erase(pa(sum[l.first],l.first));
                sum[l.first]-=l.second;
                p.insert(pa(sum[l.first],l.first));
            }
             
        }
        printf("%lld\n",maxd);
    }
     
    return 0;
}
View Code

題解:  
二分硬度,拓撲排序判斷是否可行  
代碼:  
#include<bits/stdc++.h>  
using namespace std;  
#define ll long long  
const int maxn=1e5+7;  
struct node  
{  
    int to;ll cost;  
};  
vector<node>p[maxn];  
queue<int>P;  
ll a[maxn],zz[maxn];  
bool vis[maxn];  
int n,m,xx[maxn],yy[maxn];  
bool pp(ll x)  
{  
    for(int i=0;i<=n;i++)p[i].clear();  
    memset(a,0,sizeof(a));  
    for(int i=0;i<m;i++)  
    {  
        node e;e.to=yy[i];e.cost=zz[i];  
        p[xx[i]].push_back(e);  
        e.to=xx[i];  
        p[yy[i]].push_back(e);  
        a[xx[i]]+=zz[i];  
        a[yy[i]]+=zz[i];  
    }  
    int ans=0;memset(vis,0,sizeof(vis));  
    for(int i=1;i<=n;i++)  
    {  
        if(!vis[i]&&a[i]<=x)  
        {  
            ans++;  
            vis[i]=1;  
            for(int j=0;j<p[i].size();j++)  
            {  
                int to=p[i][j].to;ll z=p[i][j].cost;  
                a[to]-=z;  
                if(!vis[to]&&a[to]<=x)P.push(to),ans++,vis[to]=1;  
            }  
        }  
    }  
    while(!P.empty())  
    {  
        int v=P.front();P.pop();  
        for(int i=0;i<p[v].size();i++)  
        {  
            node e=p[v][i];  
            a[e.to]-=e.cost;  
            if(!vis[e.to]&&a[e.to]<=x)P.push(e.to),ans++,vis[e.to]=1;  
        }  
    }  
    if(ans==n)return 1;  
    return 0;  
}  
int main()  
{  
    int T;scanf("%d",&T);  
    while(T--)  
    {  
        scanf("%d%d",&n,&m);  
        for(int i=0;i<m;i++)  
        {  
            int x,y;ll z;  
            scanf("%d%d%lld",&x,&y,&z);  
            xx[i]=x;yy[i]=y;zz[i]=z;  
        }  
        ll l=-1,r=1e18;  
        while(r-l>1)  
        {  
            ll mid=(l+r)/2;  
            if(pp(mid))r=mid;  
            else l=mid;  
        }  
        printf("%lld\n",r);  
    }  
    return 0;  
}  

北京師範大學第十六屆程序設計競賽決賽 F 湯圓防漏理論