• 启发式合并


    启发式合并

    概念

    启发式算法是基于人类的经验和直观感觉,对一些算法的优化。

    作用

    可以启发式合并更加高级的数据结构,如 (heap,~set,~splays)

    复杂度计算

    每次把个数少的合并到个数多的?复杂度 (O(min(m1,m2)))

    可是我们注意到,每次合并后个数为合并前少的部分的个数的两倍以上,每个元素最多合并 (logm) 次,总复杂度 (O(mlogm))

    当合并 (heap,~set,~splays) 等,复杂度 (O(mlog2m))(作者太弱,不会证明)

    例题1

    [HNOI2009] 梦幻布丁

    思路

    对于每一个颜色,建一条链表。然后染色就是把链短的合并到链长的。

    需要注意细节,如果把2染成3,但2的链比3的长,就需要把3的合并到2上。但是现在本应属于3的链在2上,就需要记一个该颜色的链现在在哪个颜色上,即是代码中的 (now) 数组。

    代码
    #include<cstdio>
    #define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
    #define per(i, a, b) for (register int i=(a); i>=(b); --i)
    using namespace std;
    void swap(int &x, int &y){x^=y^=x^=y;}
    const int N=1000005;
    int head[N], nxt[N], col[N], now[N], cnt[N], st[N], ans;
    
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
        for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
        return x*f;
    }
    
    void merge(int x, int y)
    {
        cnt[y]+=cnt[x]; cnt[x]=0;
        for (int i=head[x]; i; i=nxt[i])
        {
            if (col[i+1]==y) ans--;
            if (col[i-1]==y) ans--;
        }
        for (int i=head[x]; i; i=nxt[i]) col[i]=y;
        nxt[st[x]]=head[y]; head[y]=head[x];
        head[x]=st[x]=cnt[x]=0;
    }
    
    int main()
    {
        int n=read(), m=read();
        rep(i, 1, n)
        {
            col[i]=read(); now[col[i]]=col[i];
            if (col[i]^col[i-1]) ans++;
            if (!head[col[i]]) st[col[i]]=i;
            cnt[col[i]]++; nxt[i]=head[col[i]]; head[col[i]]=i;
        }
        rep(i, 1, m)
        {
            int opt=read();
            if (opt==2) printf("%d
    ", ans);
            else
            {
                int x=read(), y=read();
                if (x==y) continue;
                if (cnt[now[x]]>cnt[now[y]]) swap(now[x], now[y]);
                x=now[x]; y=now[y];
                if (cnt[x]) merge(x, y);
            }
        }
        return 0;
    }
    
    

    例题2

    [十二省联考2019]春节十二响

    思路

    考虑一条链,显然你是把两个链分别的最大值放在一起,次大值放在一起,等等

    那么如果有多个链呢?你就把第一个链和第二个链按上面的操作,得到的新的结果再和第三个链合并...

    代码
    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<vector>
    #include<algorithm>
    #define ll long long
    
    using namespace std;
    const int maxn=2e5+5;
    int a[maxn],id[maxn],tot,tp[maxn];
    
    priority_queue<int> q[maxn];
    
    inline int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    struct Edge{
    	int to,next;
    }e[maxn<<1];
    
    int head[maxn],cnt;
    void add(int u,int v){
    	e[++cnt].to=v;
    	e[cnt].next=head[u];
    	head[u]=cnt;
    }
    
    inline void dfs(int now,int fa){
    	id[now]=++tot;
    	for(int i=head[now];i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if(v==fa) continue;
    		dfs(v,now);
    		if(q[id[now]].size()<q[id[v]].size()) swap(id[now],id[v]);
    		int size=q[id[v]].size();
    		for(int j=1;j<=size;j++)
    		{
    			tp[j]=max(q[id[now]].top(),q[id[v]].top());
    			q[id[now]].pop();
    			q[id[v]].pop();
    		}
    		for(int j=1;j<=size;j++) q[id[now]].push(tp[j]);
    	}
    	q[id[now]].push(a[now]);
    }
    
    int main(){
    	int n;
    	n=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read();
    	for(int i=2;i<=n;i++)
    	{
    		int f;
    		f=read();
    		add(i,f);
    		add(f,i);
    	}
    	dfs(1,0);
    	ll ans=0;
    	while(q[id[1]].size()) ans+=q[id[1]].top(),q[id[1]].pop();
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    734. Sentence Similarity
    Rainbow Sort
    692. Top K Frequent Words
    347. Top K Frequent Elements
    12. Integer to Roman
    13. Roman to Integer
    109. Convert Sorted List to Binary Search Tree
    用表单写兴趣爱好的程序
    方法重载与方法重写的区别
    super和this的区别
  • 原文地址:https://www.cnblogs.com/jasony/p/13339521.html
Copyright © 2020-2023  润新知