您的位置:首页 > 其它

带权并查集入门

2015-10-13 18:45 232 查看
并查集的升级版———用于记录节点之间的关系,可用于判断从属,敌对或者。。。

先看三个神似的题:

codevs 4246【noip模拟赛2015,9,day2】 奶牛的身高

http://codevs.cn/problem/4246/

给定n头牛之间的身高差(共m行),判断是否矛盾

只需要在unite时维护一个差值(祖先和当前节点的差值)

sum[a]=wi+sum[B]-sum[A];
//画棵树应该好想一些
sum[a]+=sum[fa[a]];


一开始敲对了,结果看到了负数。。。

于是深思熟虑地改了一改,然后悲伤的爆0!!!。。。

#include<cstdio>
#define MAXN 30000+5
int fa[MAXN],sum[MAXN],n;
bool flag;
int find(int a)
{
if(fa[a]==a)
return a;
int t=find(fa[a]);
sum[a]=sum[a]+sum[fa[a]];
return fa[a]=t;
}
void unite(int A,int B,int wi)
{
int a=find(A);
int b=find(B);
if(a==b)
{
if(sum[A]-sum[B]!=wi)
flag=true;
return;
}
fa[a]=b;
sum[a]=wi+sum[B]-sum[A];
return ;
}
void init()
{
flag=false;
for(int i=1;i<=n;i++)
{
fa[i]=i;
sum[i]=0;
}
}
int main()
{
int T,m,a,b,wi;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&wi);
if(!flag)
unite(a,b,wi);
}
if(flag)
printf("Bessie is blind.\n");
else
printf("Bessie's eyes are good\n");
}
}


第二个:HDU 3038 How many answers are wrong

http://acm.hdu.edu.cn/showproblem.php?pid=3038

乱入的插图是神马???

题意:已知一串数字,下面是m句话:

表示A~B之间的和为S

然后看神似的代码:

#include<cstdio>
#define MAXN 200000+5
int fa[MAXN],rank[MAXN],sum[MAXN],n;
int find(int a)
{
if(a==fa[a])
return a;
int t=find(fa[a]);
sum[a]=sum[a]+sum[fa[a]];
return fa[a]=t;
}
int _abs(int a,int b)
{
if(a<b)
return b-a;
else
return a-b;
}
bool unite(int A,int B,int wi)
{
int a=find(A);
int b=find(B);
if(a==b)
{
if(_abs(sum[A],sum[B])==wi)
return false;
else
return true;
}
fa[a]=b;
sum[a]=sum[B]+wi-sum[A];
return false;
}

void init()
{
for(int i=0;i<=n;i++)
{
rank[i]=0;
fa[i]=i;
sum[i]=0;
}
}
int main()
{
int m,l,r,wi;
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
int ans=0;
sum[0]=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&l,&r,&wi);
l--;
if(unite(l,r,wi))
ans++;
}
printf("%d\n",ans);
}
}


当天下午昨晚的这个题晚上就考还爆零,我也是无语了。。。

然后是本校的X大神和我说的另一个神似。。。

BZOJ1202 狡猾的商人

http://www.lydsy.com/JudgeOnline/problem.php?id=1202

神似代码如下:

#include <cstdio>
#include <cstring>
#define MAXN 110
int fa[MAXN],sum[MAXN],n,m;
bool flag;
int find(int a)
{
if(a==fa[a])
return a;
else
{
int f=find(fa[a]);
sum[a]+=sum[fa[a]];
return fa[a]=f;
}
}
void unite(int a,int b,int wi)
{
int ra=find(a);
int rb=find(b);
if(ra==rb)
{
if(sum[b]-sum[a]!=wi)
flag=false;
return ;
}
else
{
fa[ra]=rb;
sum[ra]=sum[b]-sum[a]-wi;
}
}
void init()
{
for(int i=0;i<=n;i++)
{
fa[i]=i;
sum[i]=0;
}
}
int main()
{
int T;
int a,b,wi;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
init();
flag=true;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&wi);
if(flag)
unite(a-1,b,wi);
}
if(!flag)
printf("false\n");
else
printf("true\n");
}
return 0;
}


再来看一个与生物有关的题:

POJ 1182 食物链

http://poj.org/problem?id=1182

POJ少有的中文题,题意就不说了。。。

然而本人太弱,看完网上大神的题解差点跪了。。。

我也是佩服能把题解写的这么详细。。。OTZ

直接附代码吧。。。

#include <cstdio>
#include <cstring>
#define MAXN 50000+5
int fa[MAXN],rank[MAXN],n,k;
int find(int a)
{
if(a==fa[a])
return a;
else
{
int f=find(fa[a]);
rank[a]=(rank[a]+rank[fa[a]])%3;
return fa[a]=f;
}
}
bool unite(int a,int b,int d)
{
int ra=find(a);
int rb=find(b);
if(ra==rb)
{
if((rank[b]-rank[a]+3)%3!=d)
return true;
else
return false;
}
fa[rb]=ra;
rank[rb]=(rank[a]-rank[b]+d+3)%3;
return false;
}
void init()
{
for(int i=1;i<=n;i++)
{
rank[i]=0;
fa[i]=i;
}
}
int main()
{
int sum=0;
int d,x,y;
scanf("%d%d",&n,&k);
init();
for(int i=1;i<=k;i++)
{
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n||(x==y&&d==2))
sum++;
else if(unite(x,y,d-1))
sum++;
}
printf("%d\n",sum);
return 0;
}


然而在《挑战程序设计竞赛》中的做法相当简单

(但内存。。。):

创建三个数组,T[MAX_K],X[MAX_k],Y[MAX_k]

对于每种动物有i-A,i-B,i-C三个集合,分别代表A吃B,B吃C,C吃A

在处理时先判断合并是否会出现矛盾

若X,Y同类,合并X-A和Y-A,X-B和Y-B,X-C和Y-C

若X吃Y,合并X-A和Y-B,X-B和Y-C,X-C和Y-A

代码如下:

#include <cstdio>
#include <cstring>
#define MAXN 50000*3+5
#define MAX_K 100000
int T[MAX_K],X[MAX_K],Y[MAX_K];
int N,K,fa[MAXN];
int find(int a)
{
if(fa[a]==a)
return a;
else
return fa[a]=find(fa[a]);
}
void unite(int a,int b)
{
a=find(a);
b=find(b);
if(a==b)
return ;
else
{
fa[a]=b;
return ;
}
}
bool same(int a,int b)
{
return find(a)==find(b);
}
void init(int n)
{
for(int i=1;i<=n;i++)
fa[i]=i;
}
void solve()
{
init(3*N);
int ans=0;
for(int i=0;i<K;i++)
{
int t=T[i];
int x=X[i]-1,y=Y[i]-1;
if(x<0||N<=x||y<0||N<=y)
{
ans++;
continue;
}
if(t==1)
{
if(same(x,y+N)||same(x,y+2*N))
ans++;
else
{
unite(x,y);
unite(x+N,y+N);
unite(x+N*2,y+N*2);
}
}
else
{
if(same(x,y)||same(x,y+2*N))
ans++;
else
{
unite(x,y+N);
unite(x+N,y+2*N);
unite(x+2*N,y);
}
}
}
printf("%d\n",ans);
}
int main()
{
scanf("%d%d",&N,&K);
for(int i=0;i<K;i++)
scanf("%d%d%d",&T[i],&X[i],&Y[i]);
solve();
return 0;
}


其实除了内存差别其他的还可以



POJ 1417 True Liars

http://poj.org/problem?id=1417

一个很神的题。。。正如某大神说的,并查集怎么还扯上DP了

(+﹏+)~。。。

然后看了网上众大神对于同一算法的不同解释,

终于,终于恍然大悟!!!!

是本人太弱,不得不服

直接贴代码吧。。。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define N 605
int p,p1,p2;
int a
[2],pre
,r
;
vector<int > b
[2];
int dp
[N/2];
int cnt;
bool vis
;
int find(int a)
{
if(a!=pre[a])
{
int f=pre[a];
pre[a]=find(pre[a]);
r[a]=r[a]^r[f];
}
return pre[a];
}
void init()
{
for(int i=1;i<=p1+p2;i++)
{
pre[i]=i;
r[i]=0;
}
memset(vis,false,sizeof(vis));
memset(a,0,sizeof(a));
cnt=1;
for(int i=1;i<N;i++)
{
b[i][0].clear();
b[i][1].clear();
}
}
int main()
{
while(scanf("%d%d%d",&p,&p1,&p2)!=EOF&&p+p1+p2)
{
init();
while(p--)
{
int u,v;
char s[10];
scanf("%d%d%s",&u,&v,s);
int k=(s[0]=='n');
int ra=find(u),rb=find(v);
if(ra!=rb)
{
pre[ra]=rb;
r[ra]=r[u]^r[v]^k;
}
}
for(int i=1;i<=p1+p2;i++)
{
if(!vis[i])
{
int f=find(i);
for(int j=i;j<=p1+p2;j++)
{
if(find(j)==f)
{
vis[j]=true;
b[cnt][r[j]].push_back(j);
a[cnt][r[j]]++;
}
}
cnt++;
}

}
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<cnt;i++)
{
for(int j=p1;j>=0;j--)
{
if(j-a[i][0]>=0)
dp[i][j]+=dp[i-1][j-a[i][0]];
if(j-a[i][1]>=0)
dp[i][j]+=dp[i-1][j-a[i][1]];
}
}
if(dp[cnt-1][p1]!=1)
{
printf("no\n");
continue;
}
else
{
vector<int > ans;
ans.clear();
for(int i=cnt-1;i>=1;i--)
{
if(p1-a[i][0]>=0&&p2-a[i][1]>=0&&dp[i-1][p1-a[i][0]]==1)
{
for(int j=0;j<b[i][0].size();j++)
ans.push_back(b[i][0][j]);
p1-=a[i][0];
p2-=a[i][1];
}
else if(p1-a[i][1]>=0&&p2-a[i][0]>=0&&dp[i-1][p1-a[i][1]]==1)
{
for(int j=0;j<b[i][1].size();j++)
ans.push_back(b[i][1][j]);
p1-=a[i][1];
p2-=a[i][0];
}
}
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++)
printf("%d\n",ans[i]);
printf("end\n");
}
}
return 0;
}


不忘初心,才能始终
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: