【并查集小结】今日并查集水题

 1.CODEVS1001 舒适的路线
题目描述 Description

Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光。
Z小镇附近共有
N(1<N≤500)个景点(编号为1,2,3,…,N),这些景点被M(0<M≤5000)条道路连接着,所有道路都是双向的,两个景点之间可能有多条道路。也许是为了保护该地的旅游资源,Z小镇有个奇怪的规定,就是对于一条给定的公路Ri,任何在该公路上行驶的车辆速度必须为Vi。频繁的改变速度使得游客们很不舒服,因此大家从一个景点前往另一个景点的时候,都希望选择行使过程中最大速度和最小速度的比尽可能小的路线,也就是所谓最舒适的路线。

输入描述 Input Description

第一行包含两个正整数,N和M。
接下来的M行每行包含三个正整数:x,y和v(1≤x,y≤N,0 最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。

输出描述 Output Description

如果景点s到景点t没有路径,输出“IMPOSSIBLE”。否则输出一个数,表示最小的速度比。如果需要,输出一个既约分数。

样例输入 Sample Input

样例1
4 2
1 2 1
3 4 2
1 4

样例2
3 3
1 2 10
1 2 5
2 3 8
1 3

样例3
3 2
1 2 2
2 3 4
1 3

样例输出 Sample Output

样例1
IMPOSSIBLE

样例2
5/4

样例3
2

数据范围及提示 Data Size & Hint

N(1<N≤500)

M(0<M≤5000)

Vi在int范围内

  第一眼看觉得像最短路径,事实上这个题可以用SPFA写,详情请看日志列表里的那篇转载日志。
事实上 标准算法并查集,把边按边权排序,然后一条条添加进去,一旦起始点和结束点连通,那么当前连法就是需要找的最优方案,此时输出就好,如果所有边都查询过了还是不能连通,就输出“IMPOSSIBLE”
帖代码
#include<cstdio>  

#include<cstring>  

using namespace std;  

int n,m,hs[100007];  

bool pd[100007];  

int hash(int x)

{  

    int t=x%100007;  

    while(pd[t]&&hs[t]!=x)

 t=(t+1)%100007;  

    pd[t]=1;  

    return t;  

}  

int main(){  

    memset(pd,0,sizeof(pd));  

    scanf("%d %d",&n,&m);  

    for(int i=1;i<=n;i++)

{  

         int t;

scanf("%d",&t);  

         hs[hash(t)]=t;  

    }  

    for(int i=1;i<=m;i++)

{  

        int t;

scanf("%d",&t);  

        if(hs[hash(t)]==t) printf("YES\n");  

        else printf("NO\n");  

    }  

    return 0;  

} 


(QAQ代码似乎略凌乱。。。算了你们自己分析去吧www)
2.CODEVS2842可怜的帆帆
水题
和舒适的路线一样解法
题目描述 Description

帆帆很可爱,但RP太差。

他费尽千辛万苦逃出了迷宫,却又掉进了一座超大的迷宫。

这次,可怜的帆帆没有地图,但他在地上发现了一张发黄的纸,上面写着M行数字。

数字代表哪两个亭子间有路径,长度为多少。

帆帆在s号亭子,出口是t号亭子。(共有N个)

但帆帆太小,身体不够强壮,两亭子间走的路太长就会不舒服。

如果最长与最短路径相差太大,他也会不舒服。

求帆帆怎样最舒服地走出迷宫?

 

输入描述 Input Description

N,M

起点 终点 长度(M行)

s t

输出描述 Output Description

若没有路径,输出IMPOSSIBLE。

否则输出最小的最长和最短单条路径的比。需要输出最简分数或整数。

 

样例输入 Sample Input

3 3
1 2 10
1 2 5
2 3 8
1 3

样例输出 Sample Output

8/5

数据范围及提示 Data Size & Hint

N《=500,M《=5000。

s不等于t。



3.CODEVS3372选学霸

题目描述 Description

老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的M尽可能接近。

输入描述 Input Description

第一行,三个正整数N,M,K。

第2...K行,每行2个数,表示一对实力相当的人的编号(编号为1…N)。

输出描述 Output Description

一行,表示既不让同学们抗议,又与原来的M尽可能接近的选出学霸的数目。(如果有两种方案与M的差的绝对值相等,选较小的一种。)

样例输入 Sample Input

4 3 2

1 2

3 4

样例输出 Sample Output

2

数据范围及提示 Data Size & Hint

100%的数据N,P<=30000

 
算法:并查集+背包,并查集找完个数之后背包DP一下就好了,找一种与m的差值最小的方案就是答案
 

 
#include<cstdio>  

#include<cstring>  

using namespace std;  

int n,m,hs[100007];  

bool pd[100007];  

int hash(int x)

{  

    int t=x%100007;  

    while(pd[t]&&hs[t]!=x)

 t=(t+1)%100007;  

    pd[t]=1;  

    return t;  

}  

int main(){  

    memset(pd,0,sizeof(pd));  

    scanf("%d %d",&n,&m);  

    for(int i=1;i<=n;i++)

{  

         int t;

scanf("%d",&t);  

         hs[hash(t)]=t;  

    }  

    for(int i=1;i<=m;i++)

{  

        int t;

scanf("%d",&t);  

        if(hs[hash(t)]==t) printf("YES\n");  

        else printf("NO\n");  

    }  

    return 0;  

} 


感谢Rivendile(ZYH)大神提供的代码援助! @赵怡浩 
4.CODEVS2751军训分批
题目描述 Description

某学校即将开展军训。共有N个班级。

前M个优秀班级为了保持学习优势,必须和3位任课老师带的班级同一批。

问共有几批?

输入描述 Input Description

N,M

老师教的其他班级(M行)

输出描述 Output Description

批次数

样例输入 Sample Input

4 2

1 1 1

2 3 1

 

样例输出 Sample Output

2

数据范围及提示 Data Size & Hint

对于50%数据,N,M<=1000.

对于100%数据,N,M<=30000,N<=M.
水题,合并完集合找一找总共几个集合就行了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;int f[30001];
int num;
int find(int x)
{ 
if (f[x]!=x) 
f[x]=find(f[x]);
return f[x];
}
void union_set(int a,int b)
{ 
int t1,t2; 
t1=find(a); 
t2=find(b); 
if (t1!=t2) 
f[t2]=t1;
}
main()
{
scanf("%d%d",&n,&m); 
for (int i=1;i<=n;i++) 
f[i]=i; 
for (int i=1;i<=m;i++) 
for (int j=1;j<=3;j++)
{
int a; scanf("%d",&a); 
union_set(i,a);
} 
for (int i=1;i<=n;i++) 
if (f[i]==i) num++; 
cout<<num;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值