牛客練習賽43,C(最小生成樹)

這道題很明顯就是一道最小生成樹的題,但是我交了17次才AC。。。
本題用Prim是應該是過不了的(即使用了堆優化),只能用Kruskal過。但是用Kruskal要注意以下幾點:
1.排序問題,快排是不行的,算了一下,快排在這道題中最壞的時間頻度是1.2e8+,這樣子基本上是很難卡過去的,何況還有常數係數。解決方案是用桶排序。
2.並查集優化,本題數據量很大,如果並查集最後數據成了一條鏈,那必定是涼涼的了。解決方案是用路徑壓縮或秩優化。
3.輸入問題,用scanf會TLE,得自己寫個輸入掛(小型的就好了)。
4.滿足以上3點了以後,還不一定能夠AC,上面三點可能比較容易想到,這最後一點可能就沒那麼容易了,就是剪枝!我最後是加了兩處剪枝才過的!

另外附上桶排序的一篇講解:
https://www.jianshu.com/p/204ed43aec0c

代碼如下:

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=1e6+6;
ll T[maxn];
int head[1005];
int fa[maxn],rk[maxn],cnt;
struct edge
{
    ll u,v,w;
    int next;
}e[maxn<<3];
  
int find(int rt)
{
    int son=rt,temp;
    while(rt!=fa[rt])
        rt=fa[rt];
    while(son!=rt)	//路徑壓縮
    {
        temp=fa[son];
        fa[son]=rt;
        son=temp;
    }
    return rt;
}
  
bool Kruskal(ll t)
{
    ll sum=0;
    for(int i=0;i<=1000;++i)
    {
        if(head[i]==-1) continue;
        for(int j=head[i]; j!=-1 ;j=e[j].next)
        {
            int a=find(e[j].u), b=find(e[j].v);
            if(a!=b)
            {
                sum+=e[j].w;
                if(rk[a]>rk[b])	//秩優化
                    fa[b]=a;
                else
                {
                    fa[a]=b;
                    if(rk[a]==rk[b])
                    	++rk[b];
                }
	            if(sum>t)	//剪枝1(很重要)
	            	return 0;
            }
        }
    }
    return sum<=t;
}
  
void addedge(ll u,ll v,ll w)	//添加邊的同時,進行桶排序
{
    e[cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].next=head[w]; head[w]=cnt++;//後面兩句就是桶排序
}
  
inline void read(ll &ret)
{
    char c;
    while((c=getchar())&&(c>'9'||c<'0'));
    ret=0;
    while(c>='0'&&c<='9') ret=ret*10+c-'0', c=getchar();
}
  
int main()
{
    ll n,m,k,t;
    read(n);    read(m);
    read(k);    read(t);
    memset(head,-1,sizeof(head));
    for(int i=0;i<=n;++i)
    {
        fa[i]=i;
        rk[i]=0;
    }
    for(int i=1;i<=n;++i)
        read(T[i]);
    cnt=0;
    for(int i=1;i<=k;++i)
    {
        ll K;
        read(K);
        T[K]=0;
    }
    ll sum=0;
    for(int i=1;i<=n;++i)
    {
    	addedge(0,i,T[i]);
    	sum+=T[i];
	}
    for(int i=1;i<=m;++i)
    {
        ll u,v,w;
        read(u);    read(v);    read(w);
        addedge(u,v,w);
    }
	if(sum<=t)	//剪枝2(不知重不重要...)
	{
		printf("Yes\n");
		return 0;
	}
    if(Kruskal(t))
        printf("Yes\n");
    else printf("No\n");
    return 0;
}

小結:
1.數據嚴苛的情況下,排序問題可以考慮桶排序(在本題中,邊權最大爲1000,所以也不難想到可以建立1000個“桶”),或可以大大優化時間;
2.並查集在數據嚴苛的情況下,也要考慮優化,路徑壓縮 / 秩優化,個人感覺秩優化會好一些;
3.瘋狂TLE,走投無路時可以多想想剪枝,或者一開始在做題的時候就考慮剪枝,可能有奇效。

發佈了296 篇原創文章 · 獲贊 9 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章