NHIP(2015)解题报告(第3题)
2016-02-15 19:34
176 查看
NHIP(2015)解题报告(第3题)
第三题:树(tree)
[题意分析]
其实考的是如何合并集和,合并集合有两种方法:
方法一:
时间复杂度:o(2n)
当输入(1,2)时:
(1)找出1,2
(2)判断1,2的“祖先”是否相同。
a.相同的,就把边数加1;
b.不相同,就把大的那个“祖先”换成小的那个“祖先”,
在寻找出更原来大的“祖先”相同的,都变成小的那个祖先。
但此题的数据范围过大,这种方法会超时,不加以使用。
方法二:
时间复杂度:o(1)
根据并查集的思路,即可解决这道题:
这样可以提高效率,因此使程序不易超时:
[程序参考]
#include <iostream>
#include <fstream>
using namespace std;
ifstream fin ("tree.in");
ofstream fout ("tree.out");
#define cin fin
#define cout fout
const int mx=1000001;
int pt[mx],fa[mx],line[mx],ans=0;
int m,n,fx,fy,x,y;
int u_f(int x)
{
int root=x;
while (fa[root]!=root)
root=fa[root]; //顺藤摸瓜,找x的最前面的祖先;
while (fa[x]!=x) //将x到root这条藤上的所有点的祖先都置为root;
{
int t=fa[x]; //t为x的父节点;
fa[x]=root; //将x的父节点的祖先置为root;
x=t; //再将x置为他的父节点,然后再从父节点为出发点,重置;
}
return root; //返回祖先节点;
}
int main()
{
int i;
cin>>m>>n;
for (i=1;i<=m;i++) //初始化每个点
{
fa[i]=i; //第i个点的父亲就是他自己,
pt[i]=1; //作为1个“树”——它包含1个节点(它自己)
line[i]=0; //和0条边
}
for (i=0;i<n;i++) //输入n条边,通过并查集,找每个点所在的集合,以及每个集合的点数边数
{
cin>>x>>y;
fx=u_f(x); //找x点的祖先
fy=u_f(y); //找y点的祖先
if (fx!=fy) //加入这条边上的两点的祖先各不相同,
{
fa[fy]=fx;
//则将y点的祖先置为x点的祖先;
line[fx]+=1+line[fy];
//x点所在集合的边数=x点所在集合的边数+y点所在集合的边数+1
pt[fx]+=pt[fy];
//x点所在集合的节点数=x点所在集合的节点数+y点所在集合的节点数
} else line[fx]++; //如果x点祖先和y点祖先相同,则x点和y点在同一集合,则该集合多出1条边;
}
for (i=1;i<=m;i++) //统计m个节点中,祖先是自己并且边数不为1的祖先个数即为不同集合的数目
if (fa[i]==i && pt[i]==line[i]+1) ans++;
cout<<ans<<endl;
return 0;
}
第三题:树(tree)
[题意分析]
其实考的是如何合并集和,合并集合有两种方法:
方法一:
时间复杂度:o(2n)
Id | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
D | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Id | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
D | 1 | 2 1 | 3 | 4 | 5 | 6 | 7 |
(2)判断1,2的“祖先”是否相同。
a.相同的,就把边数加1;
b.不相同,就把大的那个“祖先”换成小的那个“祖先”,
在寻找出更原来大的“祖先”相同的,都变成小的那个祖先。
但此题的数据范围过大,这种方法会超时,不加以使用。
方法二:
时间复杂度:o(1)
根据并查集的思路,即可解决这道题:
这样可以提高效率,因此使程序不易超时:
[程序参考]
#include <iostream>
#include <fstream>
using namespace std;
ifstream fin ("tree.in");
ofstream fout ("tree.out");
#define cin fin
#define cout fout
const int mx=1000001;
int pt[mx],fa[mx],line[mx],ans=0;
int m,n,fx,fy,x,y;
int u_f(int x)
{
int root=x;
while (fa[root]!=root)
root=fa[root]; //顺藤摸瓜,找x的最前面的祖先;
while (fa[x]!=x) //将x到root这条藤上的所有点的祖先都置为root;
{
int t=fa[x]; //t为x的父节点;
fa[x]=root; //将x的父节点的祖先置为root;
x=t; //再将x置为他的父节点,然后再从父节点为出发点,重置;
}
return root; //返回祖先节点;
}
int main()
{
int i;
cin>>m>>n;
for (i=1;i<=m;i++) //初始化每个点
{
fa[i]=i; //第i个点的父亲就是他自己,
pt[i]=1; //作为1个“树”——它包含1个节点(它自己)
line[i]=0; //和0条边
}
for (i=0;i<n;i++) //输入n条边,通过并查集,找每个点所在的集合,以及每个集合的点数边数
{
cin>>x>>y;
fx=u_f(x); //找x点的祖先
fy=u_f(y); //找y点的祖先
if (fx!=fy) //加入这条边上的两点的祖先各不相同,
{
fa[fy]=fx;
//则将y点的祖先置为x点的祖先;
line[fx]+=1+line[fy];
//x点所在集合的边数=x点所在集合的边数+y点所在集合的边数+1
pt[fx]+=pt[fy];
//x点所在集合的节点数=x点所在集合的节点数+y点所在集合的节点数
} else line[fx]++; //如果x点祖先和y点祖先相同,则x点和y点在同一集合,则该集合多出1条边;
}
for (i=1;i<=m;i++) //统计m个节点中,祖先是自己并且边数不为1的祖先个数即为不同集合的数目
if (fa[i]==i && pt[i]==line[i]+1) ans++;
cout<<ans<<endl;
return 0;
}
相关文章推荐
- IOS引入第三方冲突解决办法
- iOS在线音频播放FreeStreamer
- hdoj5247找连续数【rmq】
- iOS之@selector的函数传递多个参数
- linux下redis安装
- c#里,WebBrowser实现不加载图片等控制
- c#值类型与引用类型,可空类型
- iOS之iOS键盘隐藏
- c语言:用8个2*1的小矩形横着或竖着无重叠地覆盖一个2*8的大矩形,总共有多少种方法?
- 种树郭橐驼传
- 【解决】SOUI向导生成项目(VC2013以上版本编译时)无法运行XP解决
- hdoj5249KPI【set】
- iOS之iOS键盘隐藏
- iframe根据内容自适应高度
- SharedPreferences 用法总结
- android 5.0及以上https No peer certificate
- hdoj5248序列变换【二分】
- 整体二分小结
- java多线程 并发 编程
- Stanford 机器学习笔记 Week3 Regularization