原题目:P8817 [CSP-S 2022] 假期计划 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
小熊的地图上有 n 个点,其中编号为 1 的是它的家、编号为 2,3,…,n 的都是景点。部分点对之间有双向直达的公交线路。如果点 x 与 z1、z1 与 z2、……、zk−1 与 zk、zk 与 y 之间均有直达的线路,那么我们称 x 与 y 之间的行程可转车 k 次通达;特别地,如果点 x 与 y 之间有直达的线路,则称可转车 0 次通达。
很快就要放假了,小熊计划从家出发去 4 个不同的景点游玩,完成 5 段行程后回家:家 →→ 景点 A →→ 景点 B →→ 景点 C →→ 景点 D →→ 家且每段行程最多转车 k 次。转车时经过的点没有任何限制,既可以是家、也可以是景点,还可以重复经过相同的点。例如,在景点 A →→ 景点 B 的这段行程中,转车时经过的点可以是家、也可以是景点 C,还可以是景点 D →→ 家这段行程转车时经过的点。
假设每个景点都有一个分数,请帮小熊规划一个行程,使得小熊访问的四个不同景点的分数之和最大。
输入格式
第一行包含三个正整数 n,m,k,分别表示地图上点的个数、双向直达的点对数量、每段行程最多的转车次数。
第二行包含 n−1 个正整数,分别表示编号为 2,3,…,n 的景点的分数。
接下来 m 行,每行包含两个正整数 x,y,表示点 x 和 y 之间有道路直接相连,保证 1≤x,y≤n,且没有重边,自环。
输出格式
输出一个正整数,表示小熊经过的 44 个不同景点的分数之和的最大值。
输入输出样例
输入 #1
8 8 1 9 7 1 8 2 3 6 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 1
输出 #1
27
输入 #2
7 9 0 1 1 1 2 3 4 1 2 2 3 3 4 1 5 1 6 1 7 5 4 6 4 7 4
输出 #2
7
75分bfs搜索
我们第一时间可以想到用搜索(dfs/bfs)但由于dfs会用到栈,数据大可能会爆,所以我这里用bfs(队列)搜索四个点的最大得分
预处理(bef)
链式前向星双向建边:
int n,m,k,cnt;
long long ans;
int head[20005];
int di[2505][2505];
long long w[2505];
struct node3
{
int num;
long long w;
}wi[2505][10];
struct node
{
int next,num,time;
}e[20005];
struct node2
{
int time,num;
};
void add(int fa,int a)
{
e[++cnt].num=a;
e[cnt].next=head[fa];
head[fa]=cnt;
}
queue <node2> q;
……省略
int i,x,y;
cin>>n>>m>>k;
for (i=2;i<=n;i++) cin>>w[i];
for (i=1;i<=m;i++)
{
cin>>x>>y;
add(x,y);
add(y,x);
}
判断i号点在k次中转以内能到达的所有点j(di[i][j]=1):
void bef()
{
for (int i=1;i<=n;i++)
{
int dis[2505];
memset(dis,0x3f,sizeof(dis));
q.push({0,i});
dis[i]=0;
while (q.size())
{
node2 t=q.front();
q.pop();
if (t.time>k) continue;
for (int j=head[t.num];j;j=e[j].next)
{
int to=e[j].num;
if (dis[t.num]+1<dis[to])
{
di[i][to]=1;
if (di[1][to])
{
wi[i][4].num=to;
wi[i][4].w=w[to];
sort(wi[i]+1,wi[i]+5,cmp);
}
dis[to]=dis[t.num]+1;
q.push({dis[to],to});
}
}
}
}
}
枚举
有了每个点能到达的所有点,我们就可以枚举ABCD四个景点,将景点分数相加,记录最大得分(判断条件:a!=b!=c!=d && di[1][a]==di[a][b]==di[b][c]==di[c][d]==di[d][1]==1):
void bfs()
{
int b,c,i,j;
for (b=2;b<=n;b++)
{
for (c=2;c<=n;c++)
{
if (di[b][c] && b!=c)
{
for (i=1;i<=3;i++)
{
for (j=1;j<=3;j++)
{
int a=wi[b][i].num;
int d=wi[c][j].num;
if(a!=b && a!=c && a!=d && b!=d && c!=d && a && d)
{
ans=max(ans,w[a]+w[b]+w[c]+w[d]);
}
}
}
}
}
}
}
小技巧:我们可以将条件分开判断,减少搜索次数进行剪枝(可以多拿25分)
完整代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,k,cnt;
long long ans;
int head[20005];
int di[2505][2505];
long long w[2505];
struct node
{
int next,num,time;
}e[20005];
struct node2
{
int time,num;
};
void add(int fa,int a)
{
e[++cnt].num=a;
e[cnt].next=head[fa];
head[fa]=cnt;
}
queue <node2> q;
void bef()
{
for (int i=1;i<=n;i++)
{
int dis[2505];
memset(dis,0x3f,sizeof(dis));
q.push({0,i});
dis[i]=0;
while (q.size())
{
node2 t=q.front();
q.pop();
if (t.time>k) continue;
for (int j=head[t.num];j;j=e[j].next)
{
int to=e[j].num;
if (dis[t.num]+1<dis[to])
{
di[i][to]=1;
dis[to]=dis[t.num]+1;
q.push({dis[to],to});
}
}
}
}
}
void bfs()
{
int a,b,c,d;
for (a=2;a<=n;a++)
{
if (di[1][a]==1)
{
for (c=2;c<=n;c++)
{
for (b=2;b<=n;b++)
{
if (di[a][b]==1 && di[b][c]==1 && a!=b &&b!=c && a!=c)
{
for (d=2;d<=n;d++)
{
if (di[d][1]==1 && di[c][d]==1 && a!=d && b!=d && c!=d)
{
ans=max(ans,w[a]+w[b]+w[c]+w[d]);
}
}
}
}
}
}
}
}
int main ()
{
int i,j,x,y;
cin>>n>>m>>k;
for (i=2;i<=n;i++) cin>>w[i];
for (i=1;i<=m;i++)
{
cin>>x>>y;
add(x,y);
add(y,x);
}
bef();
bfs();
cout<<ans;
return 0;
}
100分bfs搜索
预处理(bfs)同上 ↑
我们发现ABCD四个景点都是有联系的,我们只要能够走这四个景点,说明他们的di[i][j]都等于1,如果两两相互限制,例如已经知道景点A,C,那么B景点一定满足di[a][b]==di[b][c]=1,也就是说知道一个景点相邻的两个景点,就能锁定这个景点,别忘了,我们还有起点1,他也能为一个限制条件,所以我们可以推出,枚举BC景点是最优的,因为 1 ->A->B->C->D->1,我们再记录下3个与1号景点和i号景点di[][]值为1的最优值(记录3个的原因:枚举A,可能BC已经占了前两个最优值,所以D只能再记录一个值)就能进行优化
优化枚举
void bfs()
{
int b,c,i,j;
for (b=2;b<=n;b++)
{
for (c=2;c<=n;c++)
{
if (di[b][c] && b!=c)
{
for (i=1;i<=3;i++)
{
for (j=1;j<=3;j++)
{
int a=wi[b][i].num;
int d=wi[c][j].num;
if(a!=b && a!=c && a!=d && b!=d && c!=d && a && d)
{
ans=max(ans,w[a]+w[b]+w[c]+w[d]);
}
}
}
}
}
}
}
完整代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,k,cnt;
long long ans;
int head[20005];
int di[2505][2505];
long long w[2505];
struct node3
{
int num;
long long w;
}wi[2505][10];
struct node
{
int next,num,time;
}e[20005];
struct node2
{
int time,num;
};
void add(int fa,int a)
{
e[++cnt].num=a;
e[cnt].next=head[fa];
head[fa]=cnt;
}
queue <node2> q;
bool cmp(node3 a,node3 b)
{
return a.w>b.w;
}
void bef()
{
for (int i=1;i<=n;i++)
{
int dis[2505];
memset(dis,0x3f,sizeof(dis));
q.push({0,i});
dis[i]=0;
while (q.size())
{
node2 t=q.front();
q.pop();
if (t.time>k) continue;
for (int j=head[t.num];j;j=e[j].next)
{
int to=e[j].num;
if (dis[t.num]+1<dis[to])
{
di[i][to]=1;
if (di[1][to])
{
wi[i][4].num=to;
wi[i][4].w=w[to];
sort(wi[i]+1,wi[i]+5,cmp);
}
dis[to]=dis[t.num]+1;
q.push({dis[to],to});
}
}
}
}
}
void bfs()
{
int b,c,i,j;
for (b=2;b<=n;b++)
{
for (c=2;c<=n;c++)
{
if (di[b][c] && b!=c)
{
for (i=1;i<=3;i++)
{
for (j=1;j<=3;j++)
{
int a=wi[b][i].num;
int d=wi[c][j].num;
if(a!=b && a!=c && a!=d && b!=d && c!=d && a && d)
{
ans=max(ans,w[a]+w[b]+w[c]+w[d]);
}
}
}
}
}
}
}
int main ()
{
int i,x,y;
cin>>n>>m>>k;
for (i=2;i<=n;i++) cin>>w[i];
for (i=1;i<=m;i++)
{
cin>>x>>y;
add(x,y);
add(y,x);
}
bef();
bfs();
cout<<ans;
return 0;
}
95分TLE解释:
在此说明:95分一个点TLE是因为bef()中第48行代码
if (dis[t.num]+1<dis[to])
不能带“=”,不然循环次数增加就超时了
如果有帮助的话,给个赞吧👍