上午
写并查集题目”朋友“。
题目为:
通过的代码为:
#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道+的题
奥利给!!!