lch2011_yb头像
关注

洛谷p8817 假期计划 题解

P8817 题解

题目思路

45分

通过我们细致的观察,不难发现:当k=0时(即禁止转车),这道题直接就变成了一道纯DFS的题。从家开始进行深搜,直到搜索深度为5并且刚好搜索到家时停止。
以下是dfs函数

void dfs(int x,int sc,int step)
{
    if(step==5)
    {
        if(x==1)
        {
            ans=max(ans,sc);
        }
        return;
    }
    for(int i=0;i<g[x].size();i++)
    {
        if(g[x][i]==1&&step<4) continue;
        if(vis[g[x][i]]) continue;
        vis[g[x][i]]=1;
        dfs(g[x][i],sc+a[g[x][i]],step+1);
        vis[g[x][i]]=0;
    }
}

70分

经过我们那天才般的大脑的思考,我们可以想出一个天才思路:既然题目同意转车k次,那我们就把转车k次及以内能互相到达的两个点连起来当成有道路直接通达的两个点,把新图扔进刚刚的dfs里跑一遍就可以了,dfs甚至都不用修改。
以下为70分完整代码。

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
long long n,m,k;
long long a[2505];
vector<long long> g[2505];
bool vis[2505];
long long ans;
long long lenth[2505][2505];
vector<long long> g2[2505];

void dfs(long long x,long long sc,int step)
{
    if(step==5)
    {
        if(x==1)
        {
            ans=max(ans,sc);
        }
        return;
    }
    for(int i=0;i<g2[x].size();i++)
    {
        if(g2[x][i]==1&&step<4) continue;
        if(vis[g2[x][i]]) continue;
        vis[g2[x][i]]=1;
        dfs(g2[x][i],sc+a[g2[x][i]],step+1);
        vis[g2[x][i]]=0;
    }
}

int main()
{
    cin>>n>>m>>k;
    memset(lenth,-1,sizeof(lenth));
    for(int i=2;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    for(int i=1;i<=n;i++)
    {
        queue<long long> q;
        q.push(i);
        lenth[i][i]=0;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            for(int j=0;j<g[x].size();j++)
            {
                int y=g[x][j];
                if(lenth[i][y]==-1)
                {
                    lenth[i][y]=lenth[i][x]+1;
                    q.push(y);
                }
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j) continue;
            if(lenth[i][j]<=k+1&&lenth[i][j]!=-1)
            {
                g2[i].push_back(j);
            }
        }
    }
    dfs(1,0,0);
    cout<<ans;
    return 0;
}

100分

乍一眼好像没法继续优化了,但是我们可以将旅行的过程分为三步:
1、从家到B景点,
2、从B景点到C景点,
3、从C景点到家。
整个旅行过程:
家–>A–>B–>C–>D–>家
对每个点 B,只保留权值最大的 3 个合法 A 点;对每个点 C,只保留权值最大的 3 个合法 D 点。
详细注释看代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

// n个点,m条边,题目给定k
long long n,m,k;
// a[i]:i号点的权值,1号点无权重
long long a[2505];
// 原图邻接表,存无向图
vector<long long> g[2505];
// vis数组代码里没有用到,可以删掉
bool vis[2505];
// 记录最终答案,四个点权值之和的最大值
long long ans;
// lenth[i][j]:点i到点j的最短路长度,-1代表不可达
long long lenth[2505][2505];
// best[i]:保存对于i来说,满足条件的权值最大的最多3个候选点
vector<long long> best[2505];

int main()
{
    cin>>n>>m>>k;
    // 初始化距离数组全部为-1,表示初始不可达
    memset(lenth,-1,sizeof(lenth));
    // 读入2~n号点的权值(题目1号点没有权)
    for(int i=2;i<=n;i++)
    {
        cin>>a[i];
    }
    // 建无向图
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);
        g[y].push_back(x);
    }

    // ========== BFS预处理任意两点最短路 ==========
    // 对每个起点i,跑一次BFS,求出i到所有点的最短距离
    for(int i=1;i<=n;i++)
    {
        queue<long long> q;
        q.push(i);
        lenth[i][i]=0; // 自己到自己距离为0
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            // 遍历x所有相邻点
            for(int j=0;j<g[x].size();j++)
            {
                int y=g[x][j];
                // 如果y还没有被访问(距离为-1)
                if(lenth[i][y]==-1)
                {
                    lenth[i][y]=lenth[i][x]+1;
                    q.push(y);
                }
            }
        }
    }

    // ========== 预处理每个点i的Top3候选点best[i] ==========
    // best[i]存的是A:满足 dist(1,A)<=k+1 && dist(A,i)<=k+1 的权值最大的3个A
    for(int i=2;i<=n;i++)
    {
        long long mx1=-1,mx2=-1,mx3=-1; // 第一、二、三大权值
        int p1=0,p2=0,p3=0;              // 对应权值的点编号
        // 枚举所有可能的v作为候选A
        for(int v=2;v<=n;v++)
        {
            if(v==i) continue; // A不能等于i
            // 判断距离条件:1到v、v到i都可达,且长度≤k+1
            if(lenth[1][v]!=-1&&lenth[v][i]!=-1&&lenth[1][v]<=k+1&&lenth[v][i]<=k+1)
            {
                // 更新前三名,类似维护排行榜
                if(a[v]>mx1)
                {
                    mx3=mx2; p3=p2;
                    mx2=mx1; p2=p1;
                    mx1=a[v]; p1=v;
                }
                else if(a[v]>mx2)
                {
                    mx3=mx2; p3=p2;
                    mx2=a[v]; p2=v;
                }
                else if(a[v]>mx3)
                {
                    mx3=a[v]; p3=v;
                }
            }
        }
        // 把存在的前3名加入best[i],最多3个
        if(p1!=0) best[i].push_back(p1);
        if(p2!=0) best[i].push_back(p2);
        if(p3!=0) best[i].push_back(p3);
    }

    // ========== 枚举B、C,查找最优A、D ==========
    // 枚举B和C这两个中间点,要求dist(B,C) ≤ k+1
    for(int B=2;B<=n;B++)
    {
        for(int C=2;C<=n;C++)
        {
            if(B==C) continue;
            // B到C不可达或者距离超限,直接跳过
            if(lenth[B][C]==-1||lenth[B][C]>k+1) continue;

            // best[B]是B对应的候选A;best[C]是C对应的候选D
            for(int i=0;i<best[B].size();i++)
            {
                for(int j=0;j<best[C].size();j++)
                {
                    int A=best[B][i];
                    int D=best[C][j];
                    // 保证四个点A,B,C,D互不相同
                    if(A==B||A==C||A==D) continue;
                    if(D==B||D==C||D==A) continue;

                    // 计算当前四点权值总和,更新答案最大值
                    long long cur=a[A]+a[B]+a[C]+a[D];
                    ans=max(ans,cur);
                }
            }
        }
    }
    cout<<ans;
    return 0;
}

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/lch2011_yb/article/details/167128034

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--