基础/带权/种类 并查集

I - 并查集 poj 1456

问题分析:
这题有多种解法 我用是结构体 加优先队列 (结构体可以换成pair)
建立 天数 与 价格的结构体
然后按照 价格进行排序
开一个 cnt记录 所在天数(每天限制卖1中水果)
从头扫到尾部 在这期间
因为可能存在同一天但是确有多种选择
保持cnt<=结构体的.day
如果大于了就 检查 队首是否小于 新的 小就入队

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
struct node
{
	int vla;
	int d;
}ans[10005];
int cmp(node a,node b)
{
	return a.d<b.d;
}
int main()
{ 
int n;
while(cin>>n)
{
	for(int i=0;i<n;i++)
		cin>>ans[i].vla>>ans[i].d;
   sort(ans,ans+n,cmp);
   long long sum=0;
   int cnt=1;
   priority_queue<int,vector<int>,greater<int> >q;
   for(int i=0;i<n;i++)
   {
   	   if(cnt<=ans[i].d)
   	   {
   	   	q.push(ans[i].vla);
   	   	cnt++;
		  }
		  else if(q.top()<ans[i].vla)
		  {
		  	q.pop();
		  	q.push(ans[i].vla);
		  }
   }
   while(q.size())
   {
   	sum+=q.top();
   	q.pop();
   }
   cout<<sum<<endl;
}
    return 0;
}

J - 并查集 poj 1611

问题分析:并查集模板 直接套 然后求0点下的子节点个数
代码如下:

#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#define mem(a)  memset(a,0,sizeof(a))
using namespace std;
int sum[30005],r[30005];
int find(int a)
{
	if(r[a]!=a)
	r[a]=find(r[a]);
	return r[a];
}
int build(int a,int b)
{
	int x=find(a);
	int y=find(b);	
	if(x!=y)
	{
		r[y]=x;
		sum[x]+=sum[y];
	}
	return 0;
}
int main()
{
 int n,m;
 while(cin>>n>>m&&n)
    {
    	for(int i=0;i<n;i++)
    	{
    		sum[i]=1;
    		r[i]=i;
		}
		while(m--)
		{
			int t,a,b;
			cin>>t>>a;
			t--;
			while(t--)
			{
				cin>>b;
				build(a,b);
			} 
		}
		cout<<sum[find(0)]<<endl;
	}	
	return 0;
}

M - 带权并查集 hdu 3038D

问题分析:
带权并查集 核心就是更新权值
主要 公式 寻根时的更新: val[a]+=val[pr[a]];
建立新链接是的更新 val[find(b)]=val[a]+v-val[b];
同根时检查 是否符合权值
代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#define  mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn =200005;
int  val[maxn],pr[maxn];
int find(int a)
{
   if(pr[a]==-1)
   return a;
   int t=find(pr[a]);
    val[a]+=val[pr[a]];
    return pr[a]=t;
}
int main() 
{
   int n,m,ans=0;
   while(cin>>n>>m)
   {
   	ans=0;
   	mem(val,0);
mem(pr,-1);
while(m--)
{
   int a,b,v;
   cin>>a>>b>>v;
   a--;
   int x=find(a);
   int y=find(b);
   if(x!=y)
   {
   	pr[y]=x;
   	val[y]=val[a]-val[b]+v;
    } 
    else if( val[b]-val[a]!=v)
     ans++;
}
cout<<ans<<endl;
   }

   return 0;
}

N - 种类并查集 POJ 2492

.种类并查集有2中写法:
1:一种是开倍数数组记记录 检查是否冲突 不冲突就合并(双向)
2:还有一种 是依赖于一个 偏移向量数组(相当于 权值数组 大神起的名字一听就感觉霸气) 来区别种类
这个题 用了 第一种方法 并 配了 测试数据
感觉关键就是 a-b+maxn b-a+maxn建立关联但他们相对 性别来说又是相对独立的
一旦出现 相同性别sex的时候就会找到同一个根
测试输出 各个数字 含义标式:

起始值 -起始值根植-过程根植-尾根植(就是root[a]=a情况 值a/root[a])

1000 4 4
1 2
a-again        1 1000002 1000002     a-end
b-maxn|-again: 2 2     b-end
b-again        1000002 1000002      b-end

a-again        2 1000001 1000001     a-end
b-maxn|-again: 1 1000002 1000002     b-end
b-again        1000001 1000001      b-end

2 3
a-again        2 1000001 1000003 1000003     a-end
b-maxn|-again: 3 3     b-end
b-again        1000003 1000003      b-end

a-again        3 1000002 1000002     a-end
b-maxn|-again: 2 1000003 1000003     b-end
b-again        1000002 1000002      b-end

1 4
a-again        1 1000002 1000004 1000004     a-end
b-maxn|-again: 4 4     b-end
b-again        1000004 1000004      b-end

a-again        4 1000003 1000003     a-end
b-maxn|-again: 1 1000004 1000004     b-end
b-again        1000001 1000003 1000003      b-end

1 3
1000004 Scenario #1:
Suspicious bugs found!

最好是自己推理一遍,有点难以理解…我是看了好久(看自闭的呢种 0_0 … )
输入 必须要用扫描 scanf() 要不就tle了…

代码如下:(含测试部分)

#include <iostream>
#include <cstring>
#include <algorithm>
#include<cstdio>
#define  mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn =1000000;
int  p[maxn*2],flag;
int find(int a)
{
    if( p[a]!=a)
    {
    	p[a]=find(p[a]);
    	cout<<p[a]<<" ";
	}
      
    return p[a];
}
int finds(int a)
{
    if( p[a]!=a)
    {
    	p[a]=find(p[a]);
	}
      
    return p[a];
}
int build(int a,int b)
{
	  int x,y;
	  x=finds(a);
	  y=finds(b-maxn);
	  if(x==y)
	  {
	  	flag=0;
	  	return 0;
	  }
	  y=finds(b);
	  if(x!=y)
	  {
	  	p[x]=p[y];
	  }
	  /*
	  cout<<"a-again        "<<a<<" "<<p[a]<<" ";
	  x=find(a);
	  cout<<"    a-end"<<endl;
	  cout<<"b-maxn|-again: "<<b-maxn<<" "<<p[b-maxn]<<" ";
	  y=find(b-maxn);
	  cout<<"    b-end"<<endl;
	  cout<<"b-again        "<<b<<" "<<p[b]<<" ";
	  y=find(b);
	  cout<<"     b-end"<<endl<<endl;
	  */	
}
int main() 
{
    int t,n,m,ans=0;
    scanf("%d",&t);
   for(int i=0;i<t;i++)
    {
	scanf("%d%d",&n,&m);
    	ans++;
    	for(int i=1;i<=n;i++)
    	{
    		p[i]=i;
			p[i+maxn]=maxn+i; 
		} 
    	flag=1;
      while(m--)
      {
      	int a,b;
      	scanf("%d%d",&a,&b);
      	build(a,b+maxn);
      	build(b,a+maxn);
      	} 
      	printf("Scenario #%d:\n",ans);
      	if(flag)
      	printf("No suspicious bugs found!\n");
      	else
      	printf("Suspicious bugs found!\n");
      	cout<<endl;
     }
    return 0;
}

O - 种类并查集 POJ 1182

这个用就是 变量val[i] 定值 区分
这个 玩意推了好久才看明白
附上大神 全面解析
把大神的解析精简了一下面结合起来应该会更好!(这里用 val代替relation).
1.偏移数组 val[]值的含义
是由于 输入关系1 2决定(取用 输入的关系-1) 因此
规定 0:同类 1:子被吃 x->y 2: 子吃父x<-y (这样子就可以 用%3进行 压缩)
2.路径压缩算法 公式
x->y->z (子->父->爷)关系表达式(枚举推理,x->y代表 x是y 的根哈~)
val[爷]=(vla[子]+val[父])%3 (%3为了 防止大于等于3的情况出现 )
同时 可以得到 x->y(子被吃 1)=y<-x(子吃父3-2)
可以得到 x->y(子被吃 3-1)=y<-x(子吃父2)
3.集合间情关系 确定公式:
val[b]=(3- val[y]+(relation -1)+val[x])%3;(relation 缩写 r)
(b=find(y),b为y的根,输入:relation x y )

关系图
X------>Y<-------B
r-1 val[y]
X------>Y------->B
r-1 3-val[y]
A----->-X------>Y------->B
val[x] r-1 val[y]

四. 判定公式

据题意共计4种为假话
1. x>n或者y>n
2. r=2&&x==y
3.a!=b时
1) x==&&val[x]!=val[y]
2) x==2&&(3-val[x]+val[y])%3!=1

关于3-2)的图解

目前状态过程所求状态
A<—X---->Y变下方向:3-val[x]A—>X---->Y

累…总算写完了哭泣~~
代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include<cstdio>
#define  mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn =50010;
int  p[maxn],val[maxn];
int find(int a)
{
    int t;
    if(a==p[a])
    return a;
    t=p[a];
    p[a]=find(t);
    val[a]=(val[a]+val[t])%3;
    return p[a];
}
int main() 
{
     int n,m,ans=0;
scanf("%d%d",&n,&m);
	 for(int i=0;i<=n;i++)
     {
     	p[i]=i;
     	val[i]=0;
	 }
   while(m--)
   {
   	int relation,x,y;
scanf("%d%d%d",&relation,&x,&y);
   	if(x>n||y>n)
   	ans++;	
   	else if(relation==2&&x==y)
   		ans++;
   	else
   	{
   		int a=find(x),b=find(y);
   		if(a!=b)
   		{
   			p[b]=a;
   			val[b]=(3+relation-1+val[x]-val[y])%3;
		   }
		   else
		   {
		   	if(relation==1&&val[x]!=val[y])
		   	ans++;
		   	if(relation==2&&((3-val[x]+val[y])%3)!=1)
		   	ans++;
		   }
	}
   }
   printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
带权并查集(Weighted Union-Find)是一种常用的数据结构,用于解决动态连通性问题。它支持两个主要操作:合并(Union)和查找(Find)。 以下是一个使用Python实现的带权并查集示例代码: ```python class WeightedUnionFind: def __init__(self, n): self.parent = list(range(n)) self.size = [1] * n self.weight = [0] * n def find(self, x): if self.parent[x] != x: self.compress_path(x) return self.parent[x] def union(self, x, y, w): root_x = self.find(x) root_y = self.find(y) if root_x == root_y: return if self.size[root_x] < self.size[root_y]: self.parent[root_x] = root_y self.weight[root_x] = w - self.weight[x] + self.weight[y] self.size[root_y] += self.size[root_x] else: self.parent[root_y] = root_x self.weight[root_y] = -w + self.weight[x] - self.weight[y] self.size[root_x] += self.size[root_y] def compress_path(self, x): if self.parent[x] != x: self.compress_path(self.parent[x]) self.weight[x] += self.weight[self.parent[x]] self.parent[x] = self.parent[self.parent[x]] def get_weight(self, x): root = self.find(x) return self.weight[x] - self.weight[root] ``` 这个实现中,`parent`列表存储每个元素的父节点索引,`size`列表存储每个元素所在集合的大小,`weight`列表存储每个元素与其父节点的权值差。 `find()`方法用于查找元素所属的集合,并进行路径压缩优化。`union()`方法用于合并两个集合,并更新权值差。`compress_path()`方法用于路径压缩,加速后续的查找操作。`get_weight()`方法用于获取元素与其根节点的权值差。 带权并查集可以在解决一些算法问题中起到很好的作用,例如最小生成树算法中的Kruskal算法。希望这个示例代码能对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值