带权并查集

(看起来就水水的)带权并查集

前言:
小biu最近在学带全并查集,并且没在CSDN上写过博客,于是跃跃欲试。
包括POJ也是第一次用,我才知道这是PKU Online Judge(捂脸
反正也没人看,就当作自己的笔记辣!~

算法概括

基本思想是增加了一个数组r[n]来代表与根节点的关系,用取模运算%更新每次合并的结果

关键代码实现:

  • 初始化函数init:fa[i]=i; r[i]=0;
  • 合并函数unite:fa[fy]=fx; r[fy]=(r[x]-r[y]+d)%m;(个人认为这一句特别难懂)
  • 查询根节点find:r[x]=(r[x]+r[pre])%m;

其中, m是涉及到的种类,食物链是3,虫子是2 d是设计好的权值差,取决于程序员的想法 pre是当前查找结点的前一个结点

开始说例题

A.POJ1182 食物链

B.POJ2492 A Bug’s Life

入门算是食物链,然而看懂了之后才发现,这是a bug’s life的进阶版
(其实这两题大同小异啦
(说出来有一种事倍功半的感jio
以及我的源码。
调试了巨久啊,仍然在理解r[fy]=(r[x]-r[y]+3+(d-1))%3;有障碍

//食物链
#include<stdio.h>
#define MAX 50000+1
int r[MAX];
int fa[MAX];
void ini(int n)
{
	int i;
	for(i=1;i<=n;++i)
	{
		r[i]=0;
		fa[i]=i;
	}
}
int find(int x)
{
	if(fa[x]==x)
		return x;
	int pre=fa[x];
	fa[x]=find(fa[x]);
	r[x]=(r[x]+r[pre])%3;
	return fa[x];	
}
void merge(int d,int x,int y)
{
	int fx=find(x),fy=find(y);
	fa[fy]=fx;
	r[fy]=(r[x]-r[y]+3+(d-1))%3;
}

int main()
{
	int n,k,d,a,b,ans=0;
	scanf("%d%d",&n,&k);
	ini(n);
	while(k--)
	{
		scanf("%d%d%d",&d,&a,&b);
		if(a>n||b>n||(d==2&&a==b))
		{
			ans++;
			continue;
		}
		else if(find(a)==find(b))
		{
			if(d==1&&r[a]!=r[b])
				ans++;
			if(d==2&&(r[b]-r[a]+3)%3!=1)
				ans++;
		}
		else
			merge(d,a,b);
		
	}
	printf("%d\n",ans);
	return 0;
}

C.POJ1456 Supermarket 并查集与贪心

题是别人博客上看到的,拿来巩固知识
码完了代码 也AC了,愣是想不明白这题和并查集有啥关系orz
visit[ ]的作用甚至像是图的DFS遍历

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAX 10000+1
using namespace std;
struct goods
{
	int px,dx;
};
goods g[MAX];
int visit[MAX]; 
bool cmp(goods x,goods y)
{
	return x.px>y.px;
}
int main()
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		memset(visit,0,sizeof(visit));
		for(int i=1;i<=n;i++)
			scanf("%d%d",&g[i].px,&g[i].dx);
		sort(g+1,g+n+1,cmp);
		long long sum=0;
		//贪心,从利益最大的开始,放在ddl那天出售 
		for(int i=1;i<=n;i++)
		{
			for(int j=g[i].dx;j>0;j--)
			{
				if(visit[j]==0)
				{
					visit[j]=1;
					sum+=g[i].px;
					break;
				}
			}
		}
		cout << sum << endl; 	
	}
	return 0;
}

说起来终于应用了一次文件输入结束EOF的用法;当时在校赛的时候就接触到了,现在写下来

文件用[^z]结束输入,表示为EOF,判断循环退出条件如下:

  • while(scanf(“%d”,&n)!=EOF) { };
  • while(scanf("%d",&n)==0) { };
  • while(!scanf("%d",&n)) { };
  • while(~scanf("%d",&n)) { };

还有这两题但是我觉得超出了我的能力范围,正在啃题解ing~
D. POJ1733 Parity game
E. POJ1984 Navigation Nightmare
哇我写不下去了,哇我太困了,掰掰下回见

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值