zoukankan      html  css  js  c++  java
  • HDU5575 Discover Water Tank(启发式合并堆,2015ICPC上海)

    题意:

    给出一些水槽。

    用一些高度不同的隔板把水槽隔开。

    在水槽的不同位置和不同高度,有一些探测器,探测器可能返回有水或没水。

    询问是否存在一种放水方案,使得尽可能多的探测器说真话。

    放水方案需满足连通器原理。

    做法:

    从小到大枚举隔板,将隔板两侧的集合合并,对每个集合维护:
    1)f[i]:表示这个集合内部所有水槽全部溢出的情况下的最优解。
    2)g[i]:表示这个集合内部的水槽有些还被分隔的情况下的最优解。
    合并的过程中,从小到大枚举两边集合内部的探测器,模拟水位的上升,得出最优水位时的情况和完全溢出的情况对答案的影响,同时把已经被水位覆盖的探测器取出。
    设t1,t2为完全溢出后的影响,设sum1,sum2为某个水位下的最佳情况的影响。
    有:
    ft=f[L]+f[R]+t1+t2;
    gt=max(g[L],f[L]+sum1)+max(g[R],f[R]+sum2);

    ft和gt为合并后的f值和g值。

    注意,最后整个水槽完全溢出后,可能还有一些上方的探测器,需要再做一遍模拟。

    每次取最小值这个需求,用优先队列即可。

    每次合并两个队列,网上很多题解采用左偏树的方法,这里我采用的是启发式合并,好写很多。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+10;
    
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q[maxn];
    int father[maxn];
    int findfather (int x) {
    	int a=x;
    	while (x!=father[x]) x=father[x];
    	while (a!=father[a]) {
    		int z=a;
    		a=father[a];
    		father[z]=x;
    	} 
    	return x;
    }
    int _,n,m;
    int h[maxn],p[maxn];
    int g[maxn];//这个集合部分不合并的最大值
    int f[maxn];//这个集合全部合并的最大值 
    int sz[maxn];
    int main () {
    	scanf("%d",&_);
    	for (int k=1;k<=_;k++) {
    		scanf("%d%d",&n,&m);
    		for (int i=1;i<=n;i++) father[i]=i,sz[i]=0;
    		for (int i=1;i<=n;i++) while (q[i].size()) q[i].pop();
    		for (int i=1;i<n;i++) scanf("%d",h+i),p[i]=i;
    		int ans=0;
    		for (int i=1;i<=m;i++) {
    			int x,y,z;
    			scanf("%d%d%d",&x,&y,&z);
    			if (z==0) sz[x]++;
    			y++;
    			q[x].push({y,z});
    		}
    		for (int i=1;i<=n;i++) f[i]=sz[i],g[i]=sz[i];//初始化未合并的值 
    		sort(p+1,p+n,[&](int x,int y) {
    			return h[x]<h[y];
    		});
    		for (int i=1;i<n;i++) {
    			int sum1=0,t1=0;
    			int L=findfather(p[i]);
    			int R=findfather(p[i]+1);
    			while (q[L].size()) {
    				if (q[L].top().first>h[p[i]]) break;
    				if (q[L].top().second==0) {
    					t1--;
    				}
    				else {
    					t1++;
    				}
    				sum1=max(sum1,t1);
    				q[L].pop();
    			}
    			int sum2=0,t2=0;
    			while (q[R].size()) {
    				if (q[R].top().first>h[p[i]]) break;
    				if (q[R].top().second==0) {
    					t2--;
    				}
    				else {
    					t2++;
    				}
    				sum2=max(sum2,t2);
    				q[R].pop();
    			}
    			//printf("%d %d
    ",sum1,sum2);
    			int ft,gt;//合并后新的根的f值、g值
    			ft=f[L]+f[R]+t1+t2;
    			gt=max(g[L],f[L]+sum1)+max(g[R],f[R]+sum2);
    				
    			if (q[L].size()<q[R].size()) {
    				father[L]=R;
    				while (q[L].size()) q[R].push(q[L].top()),q[L].pop();
    				f[R]=ft;
    				g[R]=gt;
    			}
    			else {
    				father[R]=L;
    				while (q[R].size()) q[L].push(q[R].top()),q[R].pop();
    				f[L]=ft;
    				g[L]=gt;
    			}
    		}
    		int u=findfather(1);
    		int sum1=0,t1=0;
    		while (q[u].size()) {
    			if (q[u].top().second==0) t1--;
    			else t1++;
    			sum1=max(sum1,t1);
    			q[u].pop();
    		}
    		
    		ans=max(f[u]+sum1,g[u]);
    		printf("Case #%d: %d
    ",k,ans);
    	}
    }
  • 相关阅读:
    java内存模型
    如何保证消费者接收消息的顺序
    mysql事务隔离级别
    mysql加锁读
    mysql一致性读
    InnoDB锁
    JDK1.8中的线程池
    JDK1.8中HashMap实现
    物品推荐(基于物品的协同过滤算法)
    CRM 2013 生成自动编号
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15409206.html
Copyright © 2011-2022 走看看