专栏文章
2025夏图论小测3考试总结
个人记录参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mioyt08u
- 此快照首次捕获于
- 2025/12/03 03:22 3 个月前
- 此快照最后确认于
- 2025/12/03 03:22 3 个月前
比赛情况
| 题目 | 分数 | 总分 |
|---|---|---|
| [USACO3.2] 香甜的黄油 Sweet Butter | 80分 | 380分 |
| 【模板】负环 | 10分 | |
| [USACO09OPEN] Hide and Seek S | 10分 | |
| 营救 | 100分 | |
| [NOIP 2017 提高组] 奶酪 | 80分 | |
| 最小花费 | 0分 | |
| 道路重建 | 100分 |
题目总结
[USACO3.2] 香甜的黄油 Sweet Butter
错误原因
正确思路
题目说从任意一点到某个点i的最短距离之和要最小,那么我们就可以有两种方法:
方法一:floyd 跑一遍floyd,枚举终点i,打擂台求各点到点i的最短距离。
方法二:dijkstra 因为这题是无向边,所以我们可以枚举终点i然后跑以i为起点的dijkstra最后以同样的方法求和。
方法一:floyd 跑一遍floyd,枚举终点i,打擂台求各点到点i的最短距离。
方法二:dijkstra 因为这题是无向边,所以我们可以枚举终点i然后跑以i为起点的dijkstra最后以同样的方法求和。
AC Code(核心代码)
floyd
CPPfloyd();
ll minn=INT_MAX;
for(int i=1;i<=n;i++){
ll ans=0;
for(int j=1;j<=n;j++){
ans=ans+1ll*dp[j][i]*a[j];
}
minn=min(minn,ans);
}
cout<<minn;
dijkstra
CPPll sum=LONG_LONG_MAX;
for(int i=1;i<=n;i++){
dijkstra(i);
ll ans=0;
for(int j=1;j<=n;j++){
ans=ans+1ll*dis[j]*a[j];
}
sum=min(sum,ans);
}
cout<<sum;
【模板】负环
错误原因
1.初始化dis数组时,用for没有写0x3f3f3f3f,而是0x3f
2.t组数据没有清空vector
正确思路
本题是一个判负环的模版题,我们利用最短路最多只有总边数-1条边的特性,在spfa中维护当前边数,一边维护一遍判断是否大于总边数-1,就可以判断出有没有最短路。
AC Code
CPP#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N=2e6+10;
struct node{
int y,w;
};
int n,m,dis[N],vis[N],cnt[N];
vector<node> g[N];
bool spfa(int s){
queue<int> q;
for(int i=1;i<=n;i++){
dis[i]=0x3f3f3f3f;
vis[i]=cnt[i]=0;
}
dis[s]=0;
q.push(s);
vis[s]=1;
cnt[s]=0;
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
for(int i=0;i<g[x].size();i++){
int y=g[x][i].y,w=g[x][i].w;
if(dis[x]+w<dis[y]){
dis[y]=dis[x]+w;
cnt[y]=cnt[x]+1;
if(cnt[y]>n-1) return 1;
if(vis[y]==0){
vis[y]=1;
q.push(y);
}
}
}
}
return 0;
}
int main(){
// freopen("B.in","r",stdin);
// freopen("B.out","w",stdout);
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t; cin>>t;
while(t--){
cin>>n>>m;
for(int i=1;i<=n;i++) g[i].clear();
for(int i=1;i<=m;i++){
int x,y,w; cin>>x>>y>>w;
g[x].push_back({y,w});
if(w>=0) g[y].push_back({x,w});
}
if(spfa(1)) cout<<"YES\n";
else cout<<"NO\n";
}
return 0;
}
[USACO09OPEN] Hide and Seek S
错误原因
审题不仔细,把要求求的东西搞混淆了,导致答案错误
正确思路
这道题题目有点绕,但要求的东西十分简单,题目要我们求:以1为起点的最短路的最大值以及他的编号and有多少个与他距离一样的最短路。
看到起点一定,我们就想到dijkstra,所以我们先求一个1为起点的dijkstra,然后打擂台求出最大值,最后循环找出与他一样的最短路
看到起点一定,我们就想到dijkstra,所以我们先求一个1为起点的dijkstra,然后打擂台求出最大值,最后循环找出与他一样的最短路
AC Code
CPP#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N=2e6+10;
struct node{
int y,w;
bool operator < (const node & tmp) const
{
return w>tmp.w;
}
};
int n,m,dis[N],vis[N];
vector<node> g[N];
void dijkstra(int s){
priority_queue<node> q;
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
dis[s]=0;
q.push({s,dis[s]});
while(!q.empty()){
int x=q.top().y;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=0;i<g[x].size();i++){
int y=g[x][i].y,w=g[x][i].w;
if(dis[x]+w<dis[y]){
dis[y]=dis[x]+w;
q.push({y,dis[y]});
}
}
}
}
int main(){
// freopen("C.in","r",stdin);
// freopen("C.out","w",stdout);
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y; cin>>x>>y;
g[x].push_back({y,1});
g[y].push_back({x,1});
}
dijkstra(1);
int maxn=-1,mi;
for(int i=1;i<=n;i++){
if(maxn<dis[i]){
maxn=dis[i];
mi=i;
}
}
int ans=0;
for(int i=1;i<=n;i++){
if(dis[i]==maxn) ans++;
}
cout<<mi<<' '<<maxn<<' '<<ans;
return 0;
}
营救
正确思路
这题要我们求一条路径中最大的权值最小,我们学习过用floyd求最大值最小,但这题10000的数据肯定不能用那个神奇的三重循环,所以我们可以在dijkstra中用floyd的思路,把每次松弛的条件改成判断最大值,这样就可以达到的floyd一样的效果。
AC Code
CPP#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N=2e6+10;
struct node{
int y,w;
bool operator < (const node & tmp) const
{
return w>tmp.w;
}
};
int n,m,s,t,dis[N],vis[N];
vector<node> g[N];
void dijkstra(int s){
priority_queue<node> q;
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
dis[s]=0;
q.push({s,dis[s]});
while(!q.empty()){
int x=q.top().y;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=0;i<g[x].size();i++){
int y=g[x][i].y,w=g[x][i].w;
if(max(dis[x],w)<dis[y]){//用floyd的思路修改
dis[y]=min(dis[y],max(dis[x],w));
q.push({y,dis[y]});
}
}
}
}
int main(){
// freopen("D.in","r",stdin);
// freopen("D.out","w",stdout);
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++){
int x,y,w; cin>>x>>y>>w;
g[x].push_back({y,w});
g[y].push_back({x,w});
}
dijkstra(s);
cout<<dis[t];
return 0;
}
[NOIP 2017 提高组] 奶酪
错误原因
细节问题,数组和其他函数里有的数组没有定义成double类型,导致答案错误。
正确思路
本题是一道十分经典的并查集的题目,题目说,给定n球点,每条边之间如果距离小于他们的球心距,那么就能联通,问能否从下表面走到上表面。
既然这道题只是求能否联通,那么我们就可以用并查集来解决。首先枚举两个球,接着判断他们的距离,如果小于等于球心距,那么就unionn(i,j),最后判断find(0)和find(n+1),也就是上下表面是否在同一个集合就行了。
既然这道题只是求能否联通,那么我们就可以用并查集来解决。首先枚举两个球,接着判断他们的距离,如果小于等于球心距,那么就unionn(i,j),最后判断find(0)和find(n+1),也就是上下表面是否在同一个集合就行了。
AC Code
CPP#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N=2e6+10;
double x[N],y[N],z[N],fa[N];
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void unionn(int x,int y){
x=find(x); y=find(y);
if(x!=y){
fa[x]=y;
}
return ;
}
double dist(int i,int j){
double p1=abs(x[i]-x[j])*abs(x[i]-x[j]);
double p2=abs(y[i]-y[j])*abs(y[i]-y[j]);
double p3=abs(z[i]-z[j])*abs(z[i]-z[j]);
double q=sqrt(p1+p2+p3);
return q;
}
void solve(){
double n,h,r; cin>>n>>h>>r;
for(int i=0;i<=n+1;i++) fa[i]=i;
for(int i=1;i<=n;i++){
cin>>x[i]>>y[i]>>z[i];
if(z[i]<=r) unionn(i,0);
if(z[i]+r>=h) unionn(i,n+1);
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(dist(i,j)<=2*r) unionn(i,j);
}
}
if(find(0)==find(n+1)) cout<<"Yes\n";
else cout<<"No\n";
}
int main(){
// freopen("E.in","r",stdin);
// freopen("E.out","w",stdout);
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t; cin>>t;
while(t--){
solve();
}
return 0;
}
最小花费
错误原因
没有思考清楚,在定义优先队列时,没有修改重载运算符,导致全wa。
正确思路
这道题要我们求一个数,使他在乘以一些数后等于100,我们要求出这个数最小是多少。
看到这道题,我们想想就会发现我们可以反过来求,求他的最大到手率,在最后再用100除以他就可以了
AC Code
CPP#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N=2e6+10;
struct node{
int y;
double w;
bool operator < (const node & tmp) const
{
return w < tmp.w;
}
};
double n,m,dis[N],vis[N];
vector<node> g[N];
void dijkstra(int s){
priority_queue<node> q;
for(int i=1;i<=n;i++) dis[i]=-1e9;
memset(vis,0,sizeof vis);
dis[s]=1;
q.push({s,dis[s]});
while(!q.empty()){
ll x=q.top().y;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=0;i<g[x].size();i++){
int y=g[x][i].y;
double w=g[x][i].w;
if(dis[x]*w>dis[y]){
dis[y]=dis[x]*w;
q.push({y,dis[y]});
}
}
}
}
int main(){
// freopen("F.in","r",stdin);
// freopen("F.out","w",stdout);
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,w; cin>>x>>y>>w;
double z=(100-w)/100.0;
g[x].push_back({y,z});
g[y].push_back({x,z});
}
int a,b; cin>>a>>b;
dijkstra(a);
cout<<fixed<<setprecision(8)<<100.0/dis[b];
return 0;
}
道路重建
正确思路
这道题看起来十分简单,但实际上确实十分简单有个坑,看上去只要我们求一个最短路,但是注意,我们跑最短路的时候只有在道路被损毁的时候才有边权,所以我们可以先把边权保存,在道路被损毁时在赋值,这样就得到了一张符合题目的图,接着再跑最短路就行了。
AC Code
CPP#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int N=2e6+10;
struct node{
int y,w;
bool op;
bool operator < (const node & tmp) const
{
return w>tmp.w;
}
};
int n,m,dp[200][200],a[200][200];
vector<node> g[N];
int main(){
// freopen("G.in","r",stdin);
// freopen("G.out","w",stdout);
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
memset(dp,0x3f,sizeof dp);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,z; cin>>x>>y>>z;
a[x][y]=z;
a[y][x]=z;
dp[x][y]=0;
dp[y][x]=0;
}
int d; cin>>d;
for(int i=1;i<=d;i++){
int x,y; cin>>x>>y;
dp[x][y]=a[x][y];
dp[y][x]=a[y][x];
}
for(int i=1;i<=n;i++) dp[i][i]=0;
int a,b; cin>>a>>b;
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
}
}
}
cout<<dp[a][b];
return 0;
}
总结
这次考试有很多细节问题,之后在写完一个代码后,要检查几次,自己造几个数据测试,不能再这样大意。
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...