目录
1. 题目大意:
https://www.luogu.com.cn/problem/P8817
2. 解题思路:链表
首先预处理,存每个点从此处到达另外图中所有的点需要转车的次数
然后寻找符合小于k次的,且分值最大的一条路径
1)预处理
void bfs(int start)
{
queue<int> q;
int dist[N];
memset(dist,0x3f,sizeof(dist));
dist[start]=0;
q.push(start);
while(q.size())
{
int t=q.front();
q.pop();
if(dist[t]==k+1) continue;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+1)
{
st[start][j]=1;//标记是否能够从start节点到j节点
dist[j]=dist[t]+1;
q.push(j);
}
}
}
}
本段代码注释:
从start点开始,到每一个点的换车次数初始值为最大值,到自己的换车次数设置为零,然后进队。
如果队不为空,则判断start到 t (队头点)的换车次数是否符合规定,不符合则直接跳过节省时间。
遍历链表,如果从start点到相邻的e[i]点换成次数大于从 t 结点+1(即加了一次换车),则替换dist值并且入队,标记。
2)寻找符合条件的路径:
for(int a=2;a<=n;a++)
{
if(st[1][a])
{
for(int b=2;b<=n;b++)
{
if(st[a][b])
for(int c=2;c<=n;c++)
{
if(st[b][c] && a!=c)
{
for(int d=2;d<=n;d++)
{
if(st[c][d] && st[d][1] && a!=d && b!=d)
{
ans=max(ans,w[a]+w[b]+w[c]+w[d]);
}
}
}
}
}
}
}
三个要点:是否连通(我们在预处理时已经将大于转车次数的叉出去了),是否不等(两个点之间不等),是否为最大值
最后输出ans
但是,超时了
3)优化
优化数组和时间
数组:我们发现用二维数组时,如果输入数据规模小,那么剩下的位置就会被浪费,所以我们采用动态数组vector
时间:
void bfs(int x)
{
queue<node> q;
q.push({x,0});
while(q.size())
{
node h=q.front();
q.pop();
if(h.step==k+1) continue;
for(auto i:g[h.num ])//vector遍历
{
if(st[x][i]>h.step+1 && i!=x)
{
st[x][i]=h.step+1;
q.push({i,h.step+1});
if(st[1][i]<=k+1)
{
f[x][4]=i;
sort(f[x]+1,f[x]+5,cmp);
}
}
}
}
}
在符合从家到第i个点转车次数小于k的情况下,将从x到第4个景区的经过的点存为i,再将f[]排序,取最大值。
这里我们主要用的是枚举四个景区a b c d 中 b c 两个景点
3. 完整代码
75分未优化版代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2505,M=10005;
long long w[N];
int n,m,k;
int h[N],e[M*2],ne[M*2],idx;
int st[N][N];
long long ans=0;
void bfs(int start)
{
queue<int> q;
int dist[N];
memset(dist,0x3f,sizeof(dist));
dist[start]=0;
q.push(start);
while(q.size())
{
int t=q.front();
q.pop();
if(dist[t]==k+1) continue;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+1)
{
st[start][j]=1;
dist[j]=dist[t]+1;
q.push(j);
}
}
}
}
void add(int x,int y)//建边
{
e[++idx]=y;
ne[idx]=h[x];
h[x]=idx;
}
int main()
{
memset(h,-1,sizeof(h));
int x,y;
cin>>n>>m>>k;
for(int i=2;i<=n;i++) cin>>w[i];
for(int i=1;i<=m;i++)
{
cin>>x>>y;
add(x,y);
add(y,x);//建图
}
//预处理 任何景点之间能否k次 中转到达
for(int i=1;i<=n;i++)
{
bfs(i);
}
//枚举a b c d是否满足题干要求
for(int a=2;a<=n;a++)
{
if(st[1][a])
{
for(int b=2;b<=n;b++)
{
if(st[a][b])
for(int c=2;c<=n;c++)
{
if(st[b][c] && a!=c)
{
for(int d=2;d<=n;d++)
{
if(st[c][d] && st[d][1] && a!=d && b!=d)
{
ans=max(ans,w[a]+w[b]+w[c]+w[d]);
}
}
}
}
}
}
}
cout<<ans;
return 0;
}
满分:
#include<bits/stdc++.h>
using namespace std;
const int N=2505,M=10005;
int n,m,k;
long long w[N],ans;
int st[N][N],f[N][5];
vector<int> g[N];
struct node
{
int num,step;
};
int cmp(int a,int b)
{
return w[a]>w[b];
}
void bfs(int x)
{
queue<node> q;
q.push({x,0});
while(q.size())
{
node h=q.front();
q.pop();
if(h.step==k+1) continue;
for(auto i:g[h.num ])
{
if(st[x][i]>h.step+1 && i!=x)
{
st[x][i]=h.step+1;
q.push({i,h.step+1});
if(st[1][i]<=k+1)
{
f[x][4]=i;
sort(f[x]+1,f[x]+5,cmp);
}
}
}
}
}
int main()
{
int x,y;
cin>>n>>m>>k;
for(int i=2;i<=n;i++) cin>>w[i];
for(int i=1;i<=m;i++)
{
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);//双向建边
}
memset(st,0x3f,sizeof(st));
for(int i=1;i<=n;i++)
{
bfs(i);
}
//枚举a b c d是否满足题干要求
ans=0;
int a,b,c,d;
for(b=2;b<=n;b++)
{
for(c=2;c<=n;c++)
{
if(st[b][c]<=k+1)
{
for(int i=1;i<=3;i++)
{
for(int j=1;j<=3;j++)
{
a=f[b][i];
d=f[c][j];
if(a && d && a!=d && a!=c && b!=d)
{
ans=max(ans,w[a]+w[b]+w[c]+w[d]);
}
}
}
}
}
}
cout<<ans;
return 0;
}