学习记录12

上午

写并查集题目”朋友“。

题目为:

 通过的代码为:

#include <stdio.h>
#include <string.h>
int pre[30000],pre1[30000];
int fini(int t,int x)//并查集合并,因为我要建立两个并查集,所以我用t来把它们区分开
{
    if(t==1)
    {
        if(pre[x]==x)
            return x;
        return pre[x]=fini(t,pre[x]);
    }
    else {

        if(pre1[x]==x)
            return x;
        return pre1[x]=fini(t,pre1[x]);
    }

}
int main()
{
    int n,m,p,q;
    int ans=0,cnt=0;
    scanf("%d %d %d %d",&n,&m,&p,&q);
    for(int i=1;i<=n;i++)//并查集初始化
        pre[i]=i;
    for(int i=1;i<=m;i++)
        pre1[i]=i;
    for(int i=0;i<p;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        pre[fini(1,y)]=fini(1,x);
    }
    for(int i=0;i<q;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        x=-x;y=-y;
        pre1[fini(0,y)]=fini(0,x);
    }
    for(int i=1;i<=n;i++)
        if(fini(1,i)==fini(1,1))//为什么是要跟1的根节点相等而不是直接等于呢,仔细想想合并并查集的过程,
                                //会发现会有一种情况——小明或小红并不是根节点,根节点被其他的数取代了,
                                //如:3 1,这样的情况,按照我们设立的合并并查集的方法这样根节点就会是3了
                                //所以我们继续跟随1,但是我们要跟随1的根节点,如果1还是总根节点那当然没事,
                                //如果根节点又换了,我们也可以跟随
            ans++;
    for(int i=1;i<=m;i++)
        if(fini(0,i)==fini(0,1))
            cnt++;
    if(ans>cnt)//找出相识的男生和女生,如果数目不相同那么当然是少的一方才是凑成一对的个数,
                //数目相同那随便输出哪个都行
        printf("%d",cnt);
    else printf("%d",ans);
}

核心思想:

以并查集为根本,我们主要需要思考的是如何找出哪些人是跟小明或小红认识的,而且可能合并的后小明或小红并不是总的根节点,在合并的过程中某个人成为了根节点,那该如何计算跟小明或小红是朋友的人呢?咳咳,主要的解释在代码中的注释里。可以想到的是无论怎么样,只要他们是属于同一个根节点就代表是”朋友“,于是我们可以各自找他们的根节点,只要相同就好了。

下午

写题目:

 通过的代码为:

#include <stdio.h>
#include <string.h>
int pre[10010];
int fini(int x)//并查集合并
{
    if(pre[x]==x)
        return x;
    return pre[x]=fini(pre[x]);
}
int max(int x,int y)
{
    if(x>y)
        return x;
    return y;
}
int main()
{
    int n,m,w,c,d,dp[10010]={0},sum[10010][2]={0};
    scanf("%d %d %d",&n,&m,&w);
    for(int i=1;i<=n;i++)
        pre[i]=i;
    for(int i=1;i<=n;i++)
        scanf("%d %d",&sum[i][0],&sum[i][1]);
    for(int i=0;i<m;i++)
    {
        scanf("%d %d",&c,&d);
        pre[fini(d)]=fini(c);
    }
    for(int i=1;i<=n;i++)
    {
        if(pre[i]!=i)//判断i是否为一个根节点
        {
            sum[fini(i)][0]+=sum[i][0];//此时已知i不是根节点,那么将此节点的价值和价钱加入根节点之上
            sum[i][0]=0;//当前节点的值已经加入根节点,那么为了方便背包问题的实现,将此节点的值清零
            sum[fini(i)][1]+=sum[i][1];
            sum[i][1]=0;
        }
    }
    for(int i=1;i<=n;i++)//01背包
        for(int j=w;j>=sum[i][0];j--)
            dp[j]=max(dp[j],dp[j-sum[i][0]]+sum[i][1]);
    printf("%d ",dp[w]);
}

嗯,既然我已经写出来了,那么我就可以这样说:”一看就是用01背包问题解决的并查集题目“。直到归知道,要根据题目实现就不一样了。题目的核心一个是”01背包“,另一个就是如何让实现买了一个也得买另一个,也就是如何实现题目说的买第u朵就必须得买第v朵。嗯,并查集。将输入的要求并列购买的云朵并入一个集合中,然后我们再找。可以知道的是,如果某个云朵不是”根云朵“,那么他就是买某个云朵需要一起买是那个,于是我们使用并查集的查找方法找到它的根节点,然后把价值和价钱存放在表示此云朵价值和价钱的数组中,并且给原来存放这个云朵节点的价钱价值的数组里的值清零,这样在实现01背包的时候就容易多了。因为云朵是并列购买的,所以买哪一个都没问题,只需要存在一个位置存储他们的总值就够了。其他细节在代码注释中有解释。

晚上

录题目,写题解,写学习记录……

持续写题。

明日计划

写题,写出3道+的题

奥利给!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值