练习3记录

news/2025/3/17 19:37:19/文章来源:https://www.cnblogs.com/WDY-Hodur/p/18775647

A.公路修建

Description

现有 \(n\) 个互相不联通的城市,要求进行若干轮连接,每一轮中每个城市都会找与自己相距最近的城市连接。如果一轮当中修建的某些城市修建的路连成了一个环,就将环中最短的边删去。每一轮后联通的城市将构成城市联盟,下一轮中城市联盟作为一个城市与别的城市连接。直到所有城市联通为止。

Solution

容易发现,因为每个城市都会去找离自己最近的城市,所以如果构成了环,那么环上的每条边的长度都是相同的。因为已经联通的城市无法再互相连接,所以最终一定会形成一棵树。那么求最小生成树即可。因为空间问题,这题最好用 \(Prim\) 算法。

Code

#include<bits/stdc++.h>
#define N 5005
#define ll long long
using namespace std;
int n,m,x[N],y[N],vis[N];
double ans,g[N];
double dis(int i,int j){return sqrt((ll)(x[i]-x[j])*(x[i]-x[j])+(ll)(y[i]-y[j])*(y[i]-y[j]));
}
void prim(){vis[1]=1;for(int i=2;i<=n;i++)g[i]=dis(1,i);for(int nw=1;nw<n;nw++){double dist=0x3f3f3f3f;int id=0;for(int i=1;i<=n;i++){if(vis[i])continue;if(g[i]<dist){dist=g[i];id=i;}}for(int i=1;i<=n;i++)if(id!=i&&!vis[i])g[i]=min(g[i],dis(id,i));vis[id]=1;ans+=dist;} 
}
signed main(){cin>>n;for(int i=1;i<=n;i++)cin>>x[i]>>y[i];prim();printf("%.2lf",ans);return 0;
}

B.兽径管理

Description

\(n\) 个节点,并加入 \(w\) 条边,让你在每次加边时,输出当前最小生成树的长度,如果图不连通则输出 -1。

Solution

每次加边后跑 \(Kruskal\) 即可。

Code

#include<bits/stdc++.h>
#define N 205
using namespace std;
int n,w,f[N];
struct edge{int x,y,dis;bool operator<(const edge x)const{return dis<x.dis;}
}e[6005];
int findf(int x){if(x==f[x])return x;return f[x]=findf(f[x]);
}
signed main(){cin>>n>>w;for(int nw=1;nw<=w;nw++){cin>>e[nw].x>>e[nw].y>>e[nw].dis;if(nw<n-1){cout<<"-1\n";continue;}int ans=0;for(int i=1;i<=n;i++)f[i]=i;int m=0;sort(e+1,e+1+nw);for(int i=1;i<=nw;i++){int x=e[i].x,y=e[i].y,dis=e[i].dis;int fx=findf(x),fy=findf(y);if(fx==fy)continue;f[fx]=fy;++m;ans+=dis;if(m==n-1)break;}if(m<n-1)ans=-1;cout<<ans<<"\n";}return 0;
}

C.公路修建问题

Description

\(n\) 个点,\(m\) 条边,每条边有两种权值,1 号和 2 号,保证 1 号边的权值大于等于 2 号边。现在让你在求出一棵最小生成树使得上面至少有 \(k\) 个 1 号权值。

Solution

按照 \(Kruskal\) 的思路,先将所有边按照 1 号边的长度排序,在建完 \(k\) 条 1 号边后,将剩下的边按照 2 号权值排序,再建剩下的边。

Code

#include<bits/stdc++.h>
#define N 10005
using namespace std;
int n,m,k,ans1,f[N],cnt,nw;
vector<pair<int,int> >ans2;
struct edge{int x,y,d1,d2,id;
}e[20005];
bool cmp(edge a,edge b){if(a.d1!=b.d1)return a.d1<b.d1;return a.d2>b.d2;}
bool tmp(edge a,edge b){return a.d2<b.d2;}
int findf(int x){if(x==f[x])return f[x];return f[x]=findf(f[x]);
}
signed main(){cin>>n>>k>>m;--m;for(int i=1;i<=n;i++)f[i]=i;for(int i=1;i<=m;i++){cin>>e[i].x>>e[i].y>>e[i].d1>>e[i].d2;e[i].id=i;}sort(e+1,e+1+m,cmp);for(int i=1;i<=m;i++){int x=e[i].x,y=e[i].y,dis=e[i].d1;int fx=findf(x),fy=findf(y);if(fx==fy)continue;f[fx]=fy;ans1=dis;++cnt;ans2.push_back({e[i].id,1});if(cnt==k){nw=i+1;break;}}sort(e+nw,e+1+m,tmp);for(int i=nw;i<=m;i++){int x=e[i].x,y=e[i].y,dis=e[i].d2;int fx=findf(x),fy=findf(y);if(fx==fy)continue;f[fx]=fy;ans1=max(ans1,dis);++cnt;ans2.push_back({e[i].id,2});if(cnt==n-1)break;}cout<<ans1<<"\n";sort(ans2.begin(),ans2.end());for(int i=0;i<n-1;i++)cout<<ans2[i].first<<" "<<ans2[i].second<<"\n";return 0;
}

D.逐个击破

Description

有一棵节点数为 \(n\) 的树,断开每一条边都需要一定的代价。这 \(n\) 个节点上有 \(k\) 个是被敌人占领的,现在要求你用最小的代价将这些敌占的节点分开,使其互相不联通。

Solution

正难则反,我们把问题转化成在一个没有边的树上建边,使其成为一个森林,保证其中有 \(k\) 棵树,其中每棵树中有一个节点是敌占的,可以保证没有敌占的节点向联通。

选择用 \(Kruskal\) 建边,最终只需要 \(n-k\) 条边,在用并查集判环时,注意记录当前的这一个集合是否有敌占的节点,如果两个集合要相连就必须保证两个集合中,最多有一个是有被敌人占领的节点的。

Code

#include<bits/stdc++.h>
#define int long long
#define N 200005
using namespace std;
int n,m,isx[N],f[N],ans;
struct edge{int x,y,dis;bool operator<(const edge x)const{return dis>x.dis;}
}e[N];
int findf(int x){if(x==f[x])return x;return f[x]=findf(f[x]);
}
signed main(){cin>>n>>m;for(int i=1;i<=n;i++)f[i]=i;for(int i=1;i<=m;i++){int x;cin>>x;isx[x]=1;}for(int i=1;i<n;i++){int x,y,z;cin>>x>>y>>z;e[i]={x,y,z};ans+=z;}sort(e+1,e+n);for(int i=1;i<n;i++){int x=e[i].x,y=e[i].y,dis=e[i].dis;int fx=findf(x),fy=findf(y);if(fx==fy)continue;if(isx[fx]&&isx[fy])continue;f[fx]=fy;isx[fy]|=isx[fx];ans-=dis;}cout<<ans;return 0;
}

E.I Would Walk 500 Miles G

Description

给你 \(n\) 个奶牛,要求将它们分成 \(k\) 组,组与组之间的成员有一个距离,要求使所有的距离中最小的那个最大化,求这个值。

Solution

还是正难则反,我们可以将问题转化为建 \(n-k\) 条边,互相联通的点为一个组,然后使相连的边的最大值最小,也就相当于求 \(Kruskal\) 中最后加的那条边的下一条边的长度。

Code

#include<bits/stdc++.h>
#define N 7505
#define ll long long
#define xn 2019201913
#define yn 2019201949
#define mod 2019201997
using namespace std;
int n,m,ans,ecnt,f[N];
struct edge{int x,y;ll dis;bool operator<(const edge x)const{return dis<x.dis;}
}e[N*N/2];
int findf(int x){if(x==f[x])return x;return f[x]=findf(f[x]);
}
signed main(){cin>>n>>m;for(int i=1;i<=n;i++)f[i]=i;for(int i=1;i<n;i++){for(int j=i+1;j<=n;j++){++ecnt;e[ecnt].x=i,e[ecnt].y=j;e[ecnt].dis=((ll)i*xn+(ll)j*yn)%mod;}}sort(e+1,e+1+ecnt);int cnt=0;for(int i=1;i<=ecnt;i++){if(cnt==n-m)break;int x=e[i].x,y=e[i].y;int fx=findf(x),fy=findf(y);if(fx==fy)continue;++cnt;f[fx]=fy;}for(int i=1;i<=ecnt;i++){if(findf(e[i].x)!=findf(e[i].y)){cout<<e[i].dis;return 0;}}return 0;
}

F.网格图

Description

给定一个 \(n\times m\) 的网格图,行从 \(1~n\) 编号,列从 \(1~m\) 编号,每个点可用它所在的行编号 \(r\) 与所在的列编号 \(c\) 表示为 \((r,c)\)

\((i,j)\)\((i,j+1)\) 间连有一条权值为 \(a_i\) 的边,其中 \(1\le i\le n,1\le j<m\)

\((i,j)\)\((i+1,j)\) 间连有一条权值为 \(b_j\) 的边,其中 \(1\le i<n,1\le j\le m\)

请你求出这个网格图的最小生成树。

Solution

一条条地往里加肯定不现实,但我们发现每一排和每一列的长度都一样,用 \(Kruskal\) 的想法,我们把这个排一下序,每次只需要用这个权值乘上建的边的数量。这个数量改怎么确定?因为有判环的操作,所以肯定不是简单的乘 \(n-1\) 或乘 \(m-1\)。我们考虑什么时候会出现环:对于加横边的来说,当有不止一条横边已经建好时,且已有两条以上的竖边时,它直接建就会形成环,那么如果有 \(k\) 个竖边已经建好,就减去 \(k-1\) 条边即可;建竖边同理。同时判断是否超过 \(n\times m-1\) 条边即可。

Code

#include<bits/stdc++.h>
#define N 300005
#define int long long
using namespace std;
int n,m,ecnt,ans;
struct node{int id,x;bool operator<(const node a)const{return x<a.x;}
}a[N],b[N];
signed main(){cin>>n>>m;for(int i=1;i<=n;i++)cin>>a[i].x,a[i].id=i;for(int i=1;i<=m;i++)cin>>b[i].x,b[i].id=i;sort(a+1,a+1+n);sort(b+1,b+1+m);a[n+1].x=b[m+1].x=0x3f3f3f3f3f3f3f3f;int cnt=0;for(int i=0,j=0;(i<n||j<m)&&cnt<n*m-1;){if(a[i+1].x<b[j+1].x){++i;if(i>1){int num=m-j;if(!j)--num;if(cnt+num<=n*m-1)ans+=num*a[i].x,cnt+=num;else ans+=(n*m-1-cnt)*a[i].x,cnt=n*m-1;}else{if(cnt+m-1<=n*m-1)ans+=(m-1)*a[i].x,cnt+=m-1;else ans+=(n*m-1-cnt)*a[i].x,cnt=n*m-1; }}else{++j;if(j>1){int num=n-i;if(!i)--num;if(cnt+num<=n*m-1)ans+=num*b[j].x,cnt+=num;else ans+=(n*m-1-cnt)*b[j].x,cnt=n*m-1;}else{if(cnt+n-1<=n*m-1)ans+=(n-1)*b[j].x,cnt+=n-1;else ans+=(n*m-1-cnt)*b[j].x,cnt=n*m-1; }}}cout<<ans;return 0;
}

G.游戏

Description

\(n\) 个城市(编号为 \(0,\cdots,n−1\)),其中有些城市之间有航线。每个航线连接两个城市,并且是双向的。

梅玉问健佳,是否任意两个城市之间都可以坐飞机互达(直接或间接),健佳不想直接回答,而是要通过做游戏的方式来告诉她。梅玉可以问"城市 \(u\)\(v\) 之间有直接航线吗?",健佳会立刻直接回答该问题。梅玉会询问每对城市恰好一次,因此总计会有 \(r = \frac{n (n−1)}{2}\) 个问题。如果由前 \(i\)\(i<r\))个问题的答案可以推断出整个航空网是否连通,也就是说,是否任意一对城市之间都可以坐飞机互达(直接或间接),梅玉就获胜。否则意味着她需要知道全部 \(r\) 个回答,此时健佳获胜。

为了让游戏更好玩,他们俩同意,健佳可以随着游戏的进展而编造航空网,也就是根据梅玉此前的提问来决定此后如何作答。你的任务是,通过决定健佳如何回答,来帮助他赢得游戏。

Solution

题目要求的是在最后一个知道图是否联通,也就是在最后一个之前,图一定完全成为了两个联通块,然后我们倒着推可以发现,我们只需从后往前建边,跑最小生成树即可,说简单点就是后面回答“是”一定比前面回答“是”优。然后统计答案,能建的边就输出 1,不能建就输出 0。

Code

#include<bits/stdc++.h>
#define N 1150005
using namespace std;
int n,r,u,v,f[N],ans[N];
struct edge{int x,y,dis,id;bool operator<(const edge x){return dis>x.dis;}
}e[N];
int findf(int x){if(x==f[x])return x;return f[x]=findf(f[x]);
}
signed main(){cin>>n;r=n*(n-1)/2;for(int i=1;i<=r;i++){cin>>u>>v;e[i].x=u,e[i].y=v,e[i].dis=i;}sort(e+1,e+1+r);for(int i=0;i<n;i++)f[i]=i;for(int i=1;i<=r;i++){int x=e[i].x,y=e[i].y,id=e[i].dis;int fx=findf(x),fy=findf(y);if(fx==fy)continue;f[fx]=fy;ans[id]=1;}for(int i=1;i<=r;i++)cout<<ans[i]<<"\n"; return 0;
}

H.Kuglarz

Description

魔术师的桌子上有 \(n\) 个杯子排成一行,编号为 \(1,2,…,n\),其中某些杯子底下藏有一个小球,你现在要准确地猜出哪些杯子下是有小球的。花费 \(c_{ij}\) 元,魔术师就会告诉你杯子 \(i,i+1,…,j\) 底下藏有球的总数的奇偶性,你至少需要花费多少元,才能保证猜出哪些杯子底下藏着球?

Solution

首先我们可能会想到区间 DP,但显然无法判断当前的状态是否满足条件。换个思路,我们这么想:因为要知道每一个下面是否有,就需要通过区间的加减合并操作,来处理出每一个杯子下的奇偶性。那我们看以下两种操作:

  • \(\left[i,k \right]\)\(\left[k+1,j \right]\) 得到 \(\left[i,j \right]\)
  • \(\left[i,j \right]\)\(\left[i,k \right]\) 得到 \(\left[k+1,j \right]\)

那么我们发现,为了让其简洁一些,我们可以将其改为一个左闭右开的区间,即:

  • \(\left[i,k \right)\)\(\left[k+1,j \right)\) 得到 \(\left[i,j \right)\)
  • \(\left[i,j \right)\)\(\left[i,k \right)\) 得到 \(\left[k+1,j \right)\)

这样首位相接的形式显然可以简洁一些。

那么为了使每一个杯子都确定,我们需要保证每一个 \(\left[i,i+1\right)\) 都可以被转移出来,也就是保证每个 \(i\)\(i+1\) 都是联通的。

权值和最小,全部互相连通,直接用最小生成树即可,最后需要建 \(n\) 条边。

Code

#include<bits/stdc++.h>
#define int long long
#define N 2005
using namespace std;
int n,x,ecnt,f[N],ans;
struct edge{int x,y,dis;bool operator<(const edge x)const{return dis<x.dis;}
}e[N*N/2];
int findf(int x){if(x==f[x])return x;return f[x]=findf(f[x]);
}
signed main(){cin>>n;for(int i=1;i<=n;i++)for(int j=i;j<=n;j++){cin>>x;e[++ecnt]={i,j+1,x};}for(int i=1;i<=n+1;i++)f[i]=i;sort(e+1,e+1+ecnt);int cnt=0;for(int i=1;i<=ecnt;i++){int x=e[i].x,y=e[i].y,dis=e[i].dis;int fx=findf(x),fy=findf(y);if(fx==fy)continue;f[fx]=fy;ans+=dis;++cnt;if(cnt==n)break;}cout<<ans;return 0;
}

J.Tree I

Description

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 \(need\) 条白色边的生成树,题目保证有解。

Solution

会发现一个事情,如果我们直接按照长度排序,然后跑 \(Kruskal\) 不一定能满足 \(need\) 条白边。那么如果我们稍微改一下排序的顺序,保证排序后白边间的相对位置与黑边间的相对位置不变即可。怎么改变这个顺序?我们可以给每条白边的长度都加上一个值,然后再排序,那么一定有一个值可以达到我们想要的效果,也就是在给所有白边加上这个值后进行排序,可以使此使用 \(Kruskal\) 跑出来的最小生成树恰好有 \(need\) 条白边。至于这个值怎么找,用二分就行了。每次判断当前排序情况下的白边数量与 \(need\) 的关系,如果大于等于 \(need\),就说明白边多了,所以将这个值增加;否则减少。

但我们会发现这么一个问题,如果某个值为 \(x\),它会导致白边的数量为 \(need+1\),但是下一次二分时又成了 \(need-1\),即无法找到恰好等于 \(need\) 的情况。因为题目中说保证有解,所以数量为当白边的数量大于 \(need\) 时,有可能是改变后的白边长度与黑边长度相等,所以只需把它当作有 \(need\) 条白边就行了。

Code

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,m,need,l,r,ans,f[N];
struct edge{int x,y,dis,col;bool operator<(const edge &x)const{if(dis!=x.dis)return dis<x.dis;return col<x.col;}
}e[N];
int findf(int x){if(x==f[x])return x;return f[x]=findf(f[x]);
}
int cnt,sum,num;
void kruskal(int mid){cnt=sum=num=0;for(int i=1;i<=m;i++){if(cnt==n-1)break;int fx=findf(e[i].x),fy=findf(e[i].y);if(fx==fy)continue;f[fx]=fy;++cnt;sum+=e[i].dis;if(!e[i].col){++num;if(num<=need)sum-=mid;}}
}
signed main(){cin>>n>>m>>need;for(int i=1;i<=m;i++)cin>>e[i].x>>e[i].y>>e[i].dis>>e[i].col;l=-101,r=101;while(l<=r){int mid=(l+r)>>1;for(int i=0;i<n;i++)f[i]=i;for(int i=1;i<=m;i++)if(!e[i].col)e[i].dis+=mid;sort(e+1,e+1+m);kruskal(mid);if(num>=need){ans=sum;l=mid+1;}else r=mid-1;for(int i=1;i<=m;i++)if(!e[i].col)e[i].dis-=mid;}cout<<ans;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/900523.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PCB的通孔、盲孔、埋孔|元器件的符号和封装

他们的本质都是用来切换的层的通孔:从顶层到底层,可以看到头 盲孔:看不到头的,如图从第一层切换到了第二层 埋孔:顾名思义就是埋进去了,无论从正面还是反面都是看不到的,它是处于内层的原理图就是一个表示符号,封装是元器件具体实物大小,具体形状

在IDEA编辑器中,如何在.gitignore 的文件中,把 .ides 的文件忽略,提交git的时候不提交 .idea文件夹

方法 1:直接编辑 .gitignore 文件创建或编辑 .gitignore 文件在项目根目录(与 .git 文件夹同级)右键点击 → New → File,输入文件名 .gitignore。如果已存在 .gitignore,直接双击打开。添加忽略规则在 .gitignore 文件中添加以下内容:# 忽略所有 .idea 目录及其内容 .id…

揭秘EtherCAT转profinet玻璃制造厂的复杂生产环境与智能设备运用

玻璃制造厂的生产环境都比较复杂,需要严格的操作规程,及安全规范。玻璃制造厂的生产环境通常具有以下特点:高温环境:玻璃的熔化过程需要在高温下进行,熔炉的温度通常达到1400℃以上。因此,厂房内的设备和材料必须能够耐高温,并具备良好的隔热性能。 粉尘和化学物质:在玻…

20242943 2024-2025-2 《网络攻防实践》实验三

一.实验内容(1)动手实践了tcpdump等嗅探工具。通过嗅探工具,可以分析进入某一网站时,浏览器访问了多少个web服务器以及它们的IP地址都是什么。(2)动手实践Wireshark等抓包工具。通过使用Wireshark开源软件对在本机上以TELNET方式登录BBS进行嗅探与协议分析,得出了所登录…

20241904 2024-2025-2 《网络攻防实践》实验三

一、.实验内容动手实践tcpdump 使用tcpdump开源软件对在本机上访问www.tianya.cn网站过程进行嗅探,回答问题:在访问www.tianya.cn网站首页时,浏览器将访问多少个Web服务器? 他们的IP地址都是什么?动手实践Wireshark 使用Wireshark开源软件对在本机上以TELNET方式登录BBS进…

context的应用

1. 简介 在 Go 语言中,context 包主要用于在 并发编程 中控制和管理 goroutine 的生命周期。它提供了一种机制,可以通过传递 context.Context 来协调多个 goroutine,特别是在需要取消操作、超时控制和传递共享数据时。 2. 常见用法2.1 控制goroutine的生命周期(cancel) co…

Semantic Kernel:Phi-4 mini的tools

Phi4-mini开始支持tools了,但在第一时间试用时不理想,kenfey最近发了一篇解决方案,详见https://techcommunity.microsoft.com/blog/educatordeveloperblog/building-ai-agents-on-edge-devices-using-ollama--phi-4-mini-function-calling/4391029。本篇文章想更详细地梳理一…

Creo 11下载与安装教程

Creo是一款由PTC(Parametric Technology Corporation)开发的计算机辅助设计(CAD)软件套件。Creo包括多个模块,用于实现产品设计、建模、分析和制造过程中的各个环节。其中,Creo Parametric是其核心组件,提供了参数化建模功能,可以创建几何形状并应用关联参数,使设计过…

局域网下Python oracledb 连接远程服务器 Oracle 11g

Windows 里ipconfig 一下查服务器的IP 我的地址是在这里,F:\app\dell\product\11.2.0\dbhome_1\NETWORK\ADMIN 默认一般是在这里,C:\oracle\product\11.2.0\dbhome_1\network\admin\listener.ora# listener.ora Network Configuration File: F:\app\dell\product\11.2.0\db…

临时表空间满分析

oracle 临时表空间满,业务无法进行,临时表空间快速增长,如何分析,如何预防、如何干预快速处理临时表空间满分析 oracle 临时表空间满,业务无法进行,临时表空间快速增长通过以下方式来分析 分析步骤 1. 查看临时空间占用情况总占用情况select inst_id, username, blocks*8…

Docker镜像和容器管理

1. 镜像管理 1.1 镜像结构和原理镜像即创建容器的模版,含有启动容器所需要的文件系统及所需要的内容,因此镜像主要用于方便和快速的创建并启动容器 镜像含里面是一层层的文件系统,叫做 Union FS(联合文件系统),联合文件系统,可以将几层目录挂载到一起(就像千层饼,洋葱头…