Mr.Yu语录
星期天的大好时光不用来开始就浪费了
平时考不了试,一个周末不考试就相当于半个月不考试
于是,Perisino开始了他的最小生成树考试爆0之路
1 聪明的猴子
Desciption
在一个热带雨林中生存着一群猴子,它们以树上的果子为生。昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上。猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出水面的不同树冠上来回穿梭,以找到喜欢吃的果实。
现在,在这个地区露出水面的有N棵树,假设每棵树本身的直径都很小,可以忽略不计。我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表示(任意两棵树的坐标都不相同)。
在这个地区住着的猴子有M个,下雨时,它们都躲到了茂密高大的树冠中,没有被大水冲走。由于各个猴子的年龄不同、身体素质不同,它们跳跃的能力不同。有的猴子跳跃的距离比较远(当然也可以跳到较近的树上),而有些猴子跳跃的距离就比较近。这些猴子非常聪明,它们通过目测就可以准确地判断出自己能否跳到对面的树上。
现已知猴子的数量及每一个猴子的最大跳跃距离,还知道露出水面的每一棵树的坐标,你的任务是统计有多少个猴子可以在这个地区露出水面的所有树冠上觅食
Input
第1行为一个整数,表示猴子的个数M(2<=M<=500);
第2行为M个整数,依次表示猴子的最大跳跃距离(每个整数值在1–1000之间);
第3行为一个整数表示树的总棵数N(2<=N<=1000);
第4行至第N+3行为N棵树的坐标(横纵坐标均为整数,范围为:-1000–1000)。
(同一行的整数间用空格分开)
Output
一个整数,表示可以在这个地区的所有树冠上觅食的猴子数
Sample Input
4
1 2 3 4
6
0 0
1 0
1 2
-1 -1
-2 0
2 2
Sample Output
3
Analysis
这一道题是一道很明显的水题,是Mr.Yu体谅我们这些蒟蒻,知道我们实力有限,不想让我们爆0而出的。这一道题分析一下样例,就会发现,实际上只需要跑出一个最小生成树,然后看猴子们可不可以跳得比这一棵最小生成树的最大边还大就可以了,于是100分轻松到手
Code
/*
*t1,求一颗最小生成树,去寻找它
*最大的那一条边,再到猴子的跳跃
*距离,里面寻找有多少个比这个大就可以了
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 1003
using namespace std;
int px[maxn],py[maxn];//存坐标
struct edge{
int u,v;
double dist;
bool operator <(const edge &cmp)const{
return dist<cmp.dist;
}
}G[maxn*maxn];
int m,n;
int newp=0;
int jump[maxn];
double ans[maxn];
int father[maxn];
inline int read();
double dist_count(int a,int b);
inline int find(int x);
inline void do_union(int a,int b);
void Kruscal();
int main(){
//freopen("monkey.in","r",stdin);
//freopen("monkey.out","w",stdout);
for(int i=0;i<maxn;i++)father[i]=i;
m=read();
for(int i=1;i<=m;i++)jump[i]=read();
n=read();
for(int i=1;i<=n;i++){
px[i]=read();
py[i]=read();
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
G[++newp]=(edge){i,j,dist_count(i,j)};
}
}
Kruscal();
int l=ans[n-1];
int sum=0;
for(int i=1;i<=m;i++){
if(l<=jump[i])sum++;
}
printf("%d",sum);
}
inline int read(){
int res=0,sign=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-')sign=-1;
c=getchar();
}
res=c-48;
while((c=getchar())>='0'&&c<='9'){
res=res*10+c-48;
}
return sign*res;
}
double dist_count(int a,int b){
return sqrt((px[a]-px[b])*(px[a]-px[b])+(py[a]-py[b])*(py[a]-py[b]));
}
inline int find(int x){
if(father[x]==x)return x;
return father[x]=find(father[x]);
}
inline void do_union(int a,int b){
int f1=find(a);
int f2=find(b);
father[f1]=f2;
}
void Kruscal(){
int cnt=0;
sort(G+1,G+1+newp);
for(int i=1;i<=newp;i++){
int u=G[i].u,v=G[i].v;
if(find(u)!=find(v)){
double d=G[i].dist;
ans[++cnt]=d;
do_union(u,v);
}
if(cnt==n-1)break;
}
return;
}
2 舒适的路线
Desciption
Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光。
Z小镇附近共有
N(1<N≤500)个景点(编号为1,2,3,…,N),这些景点被M(0<M≤5000)条道路连接着,所有道路都是双向的,两个景点之间可能有多条道路。也许是为了保护该地的旅游资源,Z小镇有个奇怪的规定,就是对于一条给定的公路Ri,任何在该公路上行驶的车辆速度必须为Vi。频繁的改变速度使得游客们很不舒服,因此大家从一个景点前往另一个景点的时候,都希望选择行使过程中最大速度和最小速度的比尽可能小的路线,也就是所谓最舒适的路线。
Input
第一行包含两个正整数,N和M。
接下来的M行每行包含三个正整数:x,y和v(1≤x,y≤N,0 最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。
Output
如果景点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
Analysis
这一道题实际上第一眼看过去有点难,但是仔细想想会发现并没有这么的难,首先要解决的问题是要看\(s\)和\(t\)是否联通,我这里是单独的又去写了一个\(dfs\)来判断是否是联通的,但是呢,实际上啊,是可以在写\(Kruscal\)的时候,就看第一次可不可以联通来进行判断
那么这一道题\(Kruscal\)要怎么来写呢,实际上这一道题我们选择暴力枚举最小的速度,然后来进行最小生成树的生成,当联通了\(s\)和\(t\)以后呢就来看比值更新答案,同是还要进行跳出,因为继续生成已经是没有意义了。当计算了m-1次以后,那么就可以啦
成功的,又到手了100分
Code
//首先一次dfs看是不是联通的
//我觉得想这么多都没有什么用,
//干脆就直接暴力了吧
//虽然我的暴力可能是错误的,但是至少它过了
//样例,而且就算是全错了,还是有IMPOSSIBLE的分
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#define maxn 502
#define Int64 long long
#define fclear(n) for(int i=0;i<=n;i++)father[i]=i;
#define ansclear(n) for(int i=0;i<=n;i++)ans[i]=0;
#define rint register int
using namespace std;
struct edge{
int u,v;
Int64 w;
bool operator <(const edge &cmp)const{
return w<cmp.w;
}
}E[maxn*maxn];
int n,m,s,t;
bool vis[maxn];
int newp=0;
vector<int>G[maxn];
int father[maxn];
int ans[maxn];
inline int read();
bool dfs(int x);
void do_union(int a,int b);
int find(int x);
void Kruscal();
int main(){
//freopen("way.in","r",stdin);
//freopen("way.out","w",stdout);
n=read();m=read();
for(int i=1;i<=m;i++){
int a=read(),b=read(),c=read();
E[++newp]=(edge){a,b,c};
G[a].push_back(b);
G[b].push_back(a);
}
s=read(),t=read();
if(!dfs(s)){
printf("IMPOSSIBLE");
}
else{
Kruscal();
}
return 0;
}
inline int read(){
int res=0,sign=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-')sign=-1;
c=getchar();
}
res=c-48;
while((c=getchar())>='0'&&c<='9'){
res=res*10+c-48;
}
return sign*res;
}
bool dfs(int x){
if(x==t)return true;
vis[x]=1;
vector<int>::iterator iter=G[x].begin();
bool mark=0;
while(iter!=G[x].end()){
if(!vis[*iter])mark=dfs(*iter);
if(mark==1)return true;
iter++;
}
return 0;
}
int find(int x){
if(father[x]==x)return x;
return father[x]=find(father[x]);
}
void do_union(int a,int b){
int f1=find(a);
int f2=find(b);
father[f1]=f2;
}
void Kruscal(){
sort(E+1,E+1+newp);
int maxv=0,minv=0;
for(rint i=1;i<=m-1;i++){
fclear(n);
ansclear(n);
int cnt=0;
for(rint j=i;j<=m;j++){
int u=E[j].u,v=E[j].v;
if(find(u)!=find(v)){
int d=E[j].w;
ans[++cnt]=d;
do_union(u,v);
}
if(find(s)==find(t)){
if((maxv==0&&minv==0)||(maxv/(minv+0.0)>ans[cnt]/(ans[1]+0.0))){
maxv=ans[cnt];
minv=ans[1];
break;
}
}
}
}
if(maxv%minv==0)printf("%d",maxv/minv);
else{
int lim=sqrt(minv);
for(int i=2;i<=lim;i++){
if(minv%i==0&&maxv%i==0){
minv/=i,maxv/=i;
}
}
printf("%d/%d",maxv,minv);
}
return ;
}
3 安慰奶牛
Desciption
(洛谷有这道题)
Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路。道路被用来连接N个牧场,牧场被连续地编号为1到N。每一个牧场都是一个奶牛的家。FJ计划除去P条道路中尽可能多的道路,但是还要保持牧场之间 的连通性。你首先要决定那些道路是需要保留的N-1条道路。
第j条双向道路连接了牧场Sj和Ej(1 <= Sj <= N; 1
<= Ej <= N; Sj!= Ej),而且走完它需要Lj的时间。没有两个牧场是被一条以上的道路所连接。
奶牛们非常伤心,因为她们的交通系统被削减了。你需要到每一个奶牛的住处去安慰她们。每次你到达第i个牧场的时候(即使你已经到过),你必须花去Ci的时间和奶牛交谈。
你每个晚上都会在同一个牧场(这是供你选择的)过夜,直到奶牛们都从悲伤中缓过神来。在早上 起来和晚上回去睡觉的时候,你都需要和在你睡觉的牧场的奶牛交谈一次。这样你才能完成你的 交谈任务。
Input
第 1 行包含两个整数 N 和 P 。 接下来 N 行,每行包含一个整数 Ci。 接下来 P 行,每行包含三个整数 Sj, Ej 和 Lj。
Output
输出一个整数, 所需要的总时间(包含和在你所在的牧场的奶牛的两次谈话时间)。
Sample Input
样例1:
5 7
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
4 5 12
Sample Output
样例1:
176
Analysis
这一道题一来的话直接用边权来跑最小生成树的话那么一定是会错的。而且就算是跑出来的树恰好是正确的树,得到答案的过程也不是很明确。
因此就需要对于样例好好地分析。首先容易知道的就是因为最后得到的走的过程一定会是一棵树,那么开始的节点就选择最小的那一个。而然后,再模拟这一个过程,很容易就可以发现,每一次去安慰一群奶牛,都会把这一条边走两遍,并且要花费两个节点的时间,所以对边的权值处理为原来的两倍,并且加上两边的点的权值,这样来跑最小生成树就对了。
尽管想出来了思路,但是考试的时候数组开小了,所以只拿了50分
Code
/*
*Today,I realized my weakness.
*It's too diffcult for me to sovle
*such a problem.
*/
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define maxn 10003
#define Int64 long long
#define fclear(n) for(int i=0;i<=n;i++)father[i]=i;
using namespace std;
struct edge{
int u,v;
Int64 w;
bool operator <(const edge &cmp)const{
return w<cmp.w;
}
}E[100005];
int n,m;
int wel[maxn];
int father[maxn];
inline int read();
inline Int64 Kruscal();
inline int find(int x);
inline void do_union(int x,int y);
int main(){
//freopen("cow.in","r",stdin);
//freopen("cow.out","w",stdout);
n=read(),m=read();
int minw=0x3f3f3f;
for(int i=1;i<=n;i++){
wel[i]=read();
minw=min(wel[i],minw);
}
for(int i=1;i<=m;i++){
int a=read(),b=read();
Int64 c;
scanf("%lld",&c);
E[i]=(edge){a,b,2*c+wel[a]+wel[b]};
}
Int64 ans=Kruscal();
ans+=minw;
printf("%lld",ans);
return 0;
}
inline int read(){
int res=0,sign=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-')sign=-1;
c=getchar();
}
res=c-48;
while((c=getchar())>='0'&&c<='9'){
res=res*10+c-48;
}
return sign*res;
}
inline int find(int x){
if(father[x]==x)return x;
return father[x]=find(father[x]);
}
inline void do_union(int x,int y){
int f1=find(x);
int f2=find(y);
father[f1]=f2;
}
inline Int64 Kruscal(){
fclear(n);
sort(E+1,E+1+m);
int cnt=0;
Int64 ans=0;
for(int i=1;i<=m;i++){
int u=E[i].u,v=E[i].v;
if(find(u)!=find(v)){
do_union(u,v);
ans+=E[i].w;
cnt++;
}
if(cnt==n-1)break;
}
return ans;
}
4 最小生成树记数
Desciption
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了
Input
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了第 1 行包含两个整数 N 和 P 。 接下来 N 行,每行包含一个整数 Ci。 接下来 P 行,每行包含三个整数 Sj, Ej 和 Lj。
Output
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
Sample Input
样例1:
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
样例1:
8
Analysis
考试的时候我并没有想出来这一道题应怎么做,所以说我果断的输出了样例,然后就......后来去网上找了做法,最后是暴力\(dfs\)来看哪一些边可以换做出来的,要说明的地方我在代码里面写了注释
Code
#include<cstring>
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define maxn 102
#define fclear(n) for(int i=0;i<=n;i++)father[i]=i
#define mod 31011
using namespace std;
struct edge{
int u,v;
int w;
bool operator <(const edge &cmp)const{
return w<cmp.w;
}
}E[maxn*10];
int n,m;
struct eva{
int l,r,cnt;
}C[maxn*10];
int father[maxn];
int sum;
inline int read();
void dfs(int x,int test,int s);
bool Advanced_Kruscal(int &cnt);
int find(int x);
void combine(int a,int b);
int main(){
n=read();m=read();
for(int i=1;i<=m;i++){
int a=read(),b=read(),c=read();
E[i]=(edge){a,b,c};
}
fclear(n);
int cnt=0;
bool mark=Advanced_Kruscal(cnt);
if(!mark){
printf("0");
return 0;
//无法生成最小生成树,直接就0了
}
fclear(n);
int res=1;
for(int i=1;i<=cnt;i++){
sum=0;
dfs(i,C[i].l,0);
(res*=sum)%=mod;
for(int j=C[i].l;j<=C[i].r;j++){
int u=E[j].u,v=E[j].v;
if(find(u)!=find(v)){
combine(u,v);
}
}
}
printf("%d",res);
}
int find(int x){
return father[x]==x?x:find(father[x]);
}
void combine(int a,int b){
father[find(a)]=find(b);
}
inline int read(){
int res=0,sign=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-')sign=-1;
c=getchar();
}
res=c-48;
while((c=getchar())>='0'&&c<='9'){
res=res*10+c-48;
}
return sign*res;
}
bool Advanced_Kruscal(int &cnt){
sort(E+1,E+1+m);
int tot=0;
for(int i=1;i<=m;i++){
if(E[i].w!=E[i-1].w){
C[++cnt].l=i;
C[cnt-1].r=i-1;
}
int u=E[i].u,v=E[i].v;
if(find(u)!=find(v)){
combine(u,v);
C[cnt].cnt++;
tot++;
}
}
C[cnt].r=m;
return tot==n-1;
/*
*这里面的Kruscal算法进行计算的同时,也来生成C数组,为后面的枚举做准备
*/
}
void dfs(int x,int test,int s){
/*
*x是现在再枚举哪一个权值的边,test实在说明是哪一条边
*而s则是在记录已经选了多少条边了
*/
if(test==C[x].r+1){
if(s==C[x].cnt)sum++;
return;
}
int f1=find(E[test].u),f2=find(E[test].v);
if(f1!=f2){
combine(f1,f2);
dfs(x,test+1,s+1);
father[f1]=f1,father[f2]=f2;
/*
*这里又重新更新,是因为担心如果不进行修改的话
*可能就会对后面在枚举其他的边的时候造成影响
*/
}
dfs(x,test+1,s);
}
最后
总的来说,这一次考试自己读题出了点错误,导致第三题50分没有了,而第四题自己不知道怎么做,所以说知识上还有欠缺(还好 Mr.Yu没有靠次小生成树,那个我是真的不会写),自己下来要把着一些过手。