牛客练习赛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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章