带权并查集入门

并查集的升级版———用于记录节点之间的关系,可用于判断从属,敌对或者。。。
先看三个神似的题:
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[N][2],pre[N],r[N];
vector<int > b[N][2];
int dp[N][N/2];
int cnt;
bool vis[N];
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;
}

不忘初心,才能始终

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值