官方题解 | #郊区春游#

郊区春游

http://www.nowcoder.com/practice/75b87bec7e5c4acaaad39d9ae093dc3d

郊区春游

难度:5星

解法1

我们设 dp[i][j]dp[i][j] 为 状态值为 ii ,并以 jj 号地点为终点的路程最小值。其中状态值是指每个地点是否走过的状态的二进制,1代表走过,0代表没走过。那么转移方程是:

if((1<<k)&i==1)then dp[(1<<k)i][k]=max(dp[(1<<k)i][k],dp[i][j]+g[j][k])if((1<<k)\&i==1)then\ dp[(1<<k)|i][k]=max(dp[(1<<k)|i][k],dp[i][j]+g[j][k]),其中 g[j][k]g[j][k]jjkk 的最短路。转移可以用bfs来实现,这样保证会优先检查走过较少地点的状态,然后慢慢转移到走过较多地点的状态。

所有点的点对之间的最短路可以用floyd解决,复杂度 O(n3)O(n^3) ,状压dp的复杂度为 O(2rr)O(2^r*r)

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int INF = 1e9+7;
int g[210][210] , n , m , go[20] , r , dp[1<<16][20];

void floyd() {
    for (int i = 1 ; i <= n ; i ++) {
        for (int j = 1 ; j <= n ; j ++) {
            for (int k = 1 ; k <= n ; k ++) {
                if (g[i][k]+g[i][j] < g[k][j]) g[k][j] = g[i][k]+g[i][j];
            }
        }
    }
}

struct Node {
    int sta, crt , cost;
};
int main() {
    scanf("%d%d%d",&n,&m,&r);
    for (int i = 0 ; i < r ; i ++) {
        scanf("%d",&go[i]);
    }
    for (int i = 1 ; i <= n ; i ++) {
        for (int j = 1 ; j <= n ; j ++) {
            g[i][j] = INF;
        }
        g[i][i] = 0;
    }
    for (int i = 1 ; i <= m ; i ++) {
        int s , t , v;
        scanf("%d%d%d",&s,&t,&v);
        g[s][t] = v;
        g[t][s] = v;
    }
    floyd();

    memset(dp,0x3f3f3f,sizeof dp);
    deque<Node >dq;
    int res = INF;
    for (int i = 0 ; i < r ; i ++) {
        dq.push_back({(1<<i),i,0});
        dp[1<<i][i]=0;
    }
    while (!dq.empty()) {
        auto temp = dq.front();
        dq.pop_front();
        if (temp.sta == (1<<r)-1) continue;
        for (int i = 0 ; i < r ; i ++) {
            
            if (((temp.sta & (1<<i)) == 0) && temp.cost+g[go[i]][go[temp.crt]] < dp[temp.sta|(1<<i)][i]) {
                dp[temp.sta|(1<<i)][i]=temp.cost+g[go[i]][go[temp.crt]];
                dq.push_back({(temp.sta|(1<<i)),i,dp[temp.sta|(1<<i)][i]});
            }
        }
    }
    for(int i=0;i<r;i++){
        res=min(res,dp[(1<<r)-1][i]);
    }
    printf("%d\n",res);
}

解法2

先使用 floyd 算法得出各点之间的最短距离。

我们使用状态压缩的动态规划,由于一共有最多 1515 个点需要遍历,因此有 2152^{15} 个状态需要记录。每个状态码的第 ii 二进制位的 0011 分别表示第 ii 点是否已经遍历。

dp[i][j]dp[i][j] 表示状态码是 ii 时最后一次到达 jj 点时最少花费是多少。

我们枚举此题中的所有状态码 ii 和当前状态码下的所有可能起点 jj 和终点 k k 因此可得状态转移方程 dp[i+2k][k]=min(dp[i+2k][k],dp[i][j]+dis[j][k])dp[i+2^k][k] = min(dp[i+2^k][k],dp[i][j]+dis[j][k]) (当 jj 在此状态码中已包含且 kk 不在此状态码中)。然后遍历最终状态(已经过路线上中每个点)的所有可能终点的最小值 min(dp[(2r1)][i])min(dp[(2^r-1)][i]) 即为答案。

#include<bits/stdc++.h>
#define ll long long
// #define int ll
using namespace std;

const int INF = 1e9+7;
//g-邻接矩阵  go-映射路线上第i个点实际是第几个点  dp[i][j]-i-状态码-j-终点
int g[210][210] , n , m , go[20] , r , dp[1<<16][210];

void floyd() {
    for (int k = 1 ; k <= n ; k ++) {
        for (int i = 1 ; i <= n ; i ++) {
            for (int j = 1;  j <= n ; j ++) {
                if (g[i][k]+g[k][j] < g[i][j]) g[i][j] = g[i][k]+g[k][j];
            }
        }
    }
}
int main() {
    scanf("%d%d%d",&n,&m,&r);
    for (int i = 0 ; i < r ; i ++) {
        scanf("%d",&go[i]);
    }
    for (int i = 1 ; i <= n ; i ++) {
        for (int j = 1 ; j <= n ; j ++) {
            g[i][j] = INF;
        }
        g[i][i] = 0;
    }
    for (int i = 1 ; i <= m ; i ++) {
        int s , t , v;
        scanf("%d%d%d",&s,&t,&v);
        g[s][t] = min(v,g[s][t]);
        g[t][s] = g[s][t];
    }
    floyd();
    memset(dp,0x3f3f3f,sizeof dp);
    int res = INF;
    // 从路线上第i个点出发,由题意从此步费用是0
    for (int i = 0 ; i <r ; i ++) dp[(1<<i)][go[i]] = 0;

    //遍历状态
    for (int i = 1 ; i < (1<<r)-1 ; i ++) {
        //起点j
        for (int j = 0 ; j < r ; j ++) {
            //保证j一定在i状态中
            if ((1<<j)&i) {
                for (int k = 0 ; k < r ; k ++) {
                    //保证k在此状态下没有被遍历过
                    if (!((1<<k)&i)) {
                        dp[i|(1<<k)][go[k]] = min(dp[i][go[j]]+g[go[j]][go[k]],dp[i|(1<<k)][go[k]]);
                    }
                }
            }
        }
    }
    for (int i = 0 ; i < r ; i ++) res = min(res,dp[(1<<r)-1][go[i]]);
    printf("%d\n",res);
}
全部评论
R个状态就可以了吧
点赞 回复 分享
发布于 05-27 19:24 湖南

相关推荐

面试官全程关摄像头1.自我介绍一下2.React和Vue哪个更熟悉一点3.你在之前那段实习经历中有没有什么技术性的突破(我只是实习了44天工作28天,我把我能说的都说了)4.你封装的哪个表单组件支不支持动态传值5.自己在实习阶段Vue3项目封装过hook吗6.hook有什么作用7.Vue2和Vue3的响应式区别(我说一个是proxy是拦截所有的底层操作,Object.defineProperty本身就是一个底层操作,有些东西拦截不了,比如数组的一些操作还有等等,面试官就说实在要拦截能不能拦截????我心想肯定不行呀,他的底层机制就不允许吧)8.pinia和vuex的区别(这个回答不出来是我太久没用了)9.pinia和zustand的区别,怎么选(直接给我干懵了)(我说react能用pinia吗&nbsp;&nbsp;他说要用的话也可以)10.渲染一万条数据,怎么解决页面卡顿问题(我说分页、监听滚轮动态加载,纯数据展示好像还可以用canvas画)(估计是没说虚拟表单,感觉不满意)11.type和interface的区别12.ts的泛型有哪些作用(我就说了一个结构相同但是类型不同的时候可以用,比如请求响应的接口,每次的data不同,这里能用一个泛型,他问我还有什么)13.你项目用的是React,如果让你再写一遍你会选择什么14.pnpm、npm、yarn的区别15.dependencies和devdependencies的区别总而言之太久没面试了,上一段实习的面试js问了很多。结果这次js一点没问,网络方面也没考,表现得很一般,但是知道自己的问题了&nbsp;&nbsp;好好准备,等待明天的影石360和周四的腾讯了&nbsp;&nbsp;加油!!!
解zj:大三的第一段面试居然是这样的结局
查看15道真题和解析
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
4
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务