题解:兴建高铁

题目描述

中央钦定在 HA 省建立国家级中心城市群,刚获得国家拨款兴建高铁,高铁的起止城市是中央决定的,中途可能经过若干城市。根据国家拨款的政策,国家将负担费用最大的两个区间,其余的必须由 HA 省负担。假如高铁线路中途只经过一个城市,国家只负担费用较大的区间。假如是直达的,国家将不负担任何费用。你被省里钦定为这个高铁兴建项目的总工程师,必须规划出一条高铁线路,使得 HA 省负担的费用最少。当然,路线上每个城市最多只经过一次。

输入输出格式

输入格式:

第一行是一个正整数 n,代表每对城市之间的高铁建设费用估算(注意并非每
对城市之间的建设费用都进行了估算)。接下来 n 行是用空格分隔的三个整数 s,e,c。s 和 e 代表城市的编码,高铁的起点和终点城市分别是编码为 0 和 1,其余的城市依次按顺序编码。
c<=1000,是在 s 和 e 之间建设费用估算,从 s 到 e 与从 e 到 s 的建设费用是相同的。

输出格式:

输出只有一行,格式为 c1 c2 … cm cost,各数字用一个空格分隔,代表高铁线路规划和省负担的费用。ci 代表城市编码(注意 c1=0,cm=1),cost 是费用。我们保证输入肯定有解,如果有多个解,输出当中经过城市最少的解,如果仍有多个解,则输出当中按字典序排列最小的解。

输入输出样例

输入样例:
7
0 2 10
0 3 6
2 4 5
3 4 3
3 5 4
4 1 7
5 1 8
输出样例:
0 3 4 1 3

让我们当题解一样地检查此题
先来看空间:区区几万个int不会炸的
然后看数组: 既然题目已经讲了各个城市按次序输出,那么城市数量应
严格小于n的范围,即小于50,然而我们为了避免出现一些滑稽的测试点,特意在空间允许的范围内多开了一些
然后看时间和算法:
我们特意注意了可以从大的城市往小的走,所以道路是双向的
我们双向记录下可以互通的城市,并双向记录花费,避免不必要的搜索,我们记录下城市编号的最大值
那么题目保证有解,而且要花费最小,城市数目最少,字典序最小的解
深搜的时候就应该按字典序来枚举各个城市,并用数组记录下钱(因为政府有补助)
当到达终点后,就应该判断是否能更新最优解了
于是我们用个函数来执行这个功能,先判断这条路线经过了几座城市,然后按照城市的数目
分门别类地领取政府补助(算自己要出的钱)
于是只有当钱更少或者钱一样多但是经过城市数更少的时候我们更新最优解
深搜的时间复杂度与广搜一样(这题一定要把所有的道路遍历才可以得出最优解)
应该在限定时间内可以跑过,毕竟只有五十条路,有些路还不通(表示不会算这种题目的时间,求路过大佬来评论一下)

直接上代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int minn=100000000,best[3000],len;//best用来存储最优路径 
int cnt=0,jilu[3000],tot=0,sum[3000],c[3000];//cnt用来记录城市数,jilu用来记录城市路劲,sum用来记录钱数 
int n,m[3000][3000],b[3000][3000],pd[3000],maxx=0,maxxx=0;
void print(int k);
void dfs(int t)
{
    if(tot-maxxx*2>minn)return;//如果花费已经大于最小值了,当搜到的解已经比最优解差的时候就没必要再搜了啊,这剪枝就算再粗略也能额外过三个点啊 
    for(int i=1;i<=maxx;i++)
    {
        if(b[i][t]==1&&pd[i]==0)
        {
            //cout<<"hello"<<endl;
            cnt++;
            jilu[cnt]=i;//记录路过的城市 
            sum[cnt]=m[i][t];//记录花费的钱
            tot+=sum[cnt];
            pd[i]=1;
            if(i==1)
            {
                print(cnt);
            }
            else dfs(i);
            tot-=sum[cnt];
            pd[i]=0;
            cnt--;
        }
    }
}
void print(int k)//传递经过的城市数量 
{
    //cout<<"hello"<<endl;这里没有执行哎 
    if(k==1)
    {
        if(minn>sum[1]||(minn==sum[1]&&k<len))//两种情况要更新答案:1.钱数小 2.经过城市数少 
        {
            minn=sum[1];
            best[0]=0;
            best[1]=1;
            len=1;
        }
    }
    if(k==2)
    {
        int w=min(sum[1],sum[2]);
        if(minn>w||(minn==w&&k<len))
        {
            minn=w;
            best[0]=0;
            best[1]=jilu[1];
            best[2]=1;
            len=2;
        }
    }
    if(k>2)
    {
        for(int i=1;i<=k;i++)c[i]=sum[i];
        int s=0;
        sort(c+1,c+k+1);
        for(int i=1;i<=k-2;i++)
        {
            s+=c[i];
        }
        if(minn>s||(s==minn&&k<len))
        {
            minn=s;
            for(int i=1;i<=k;i++)best[i]=jilu[i];
            len=k;
        }
    }
}
int main()
{
    //freopen("highrail.in","r",stdin);
    //freopen("highrail.out","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        b[x][y]=b[y][x]=1;
        m[x][y]=m[y][x]=z;
        maxx=max(y,max(maxx,x));//用来记录城市最大的编码,方便后面字典序查找 
        maxxx=max(maxxx,z);
    }
    pd[0]=1;//表示序号为0的城市我已经走过 
    dfs(0);
    for(int i=0;i<=len;i++)
    {
        cout<<best[i]<<' ';
    } 
    cout<<minn<<endl;
    return 0;
}

时间可能不是最优,但是还是勉强可以过的,如果以后有时间会对以前发的博客代码进行尽量优化的(难度大神不都是这么做的吗?现在看看A+Bproblem都有人用SPLAY做,滑稽),望读者共勉
Thanks for your attention.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!对于扫雷游戏的题解,我可以给你一些思路和代码示例。首先,你需要了解扫雷游戏的规则和要求。接下来,你可以使用C++语言来实现游戏逻辑和界面。 下面是一个简单的扫雷游戏的C++代码示例: ```cpp #include <iostream> #include <vector> #include <random> using namespace std; class MinesweeperGame { private: int rows; int cols; vector<vector<char>> board; vector<vector<bool>> revealed; vector<pair<int, int>> directions = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}; public: MinesweeperGame(int m, int n, int mineCount) { rows = m; cols = n; board.resize(rows, vector<char>(cols, ' ')); revealed.resize(rows, vector<bool>(cols, false)); placeMines(mineCount); calculateNumbers(); } void printBoard() { cout << " "; for (int j = 0; j < cols; j++) { cout << j << " "; } cout << endl; for (int i = 0; i < rows; i++) { cout << i << " |"; for (int j = 0; j < cols; j++) { cout << board[i][j] << "|"; } cout << endl; } } bool isGameOver() { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (board[i][j] == 'M' && revealed[i][j]) { return true; } } } return false; } void reveal(int row, int col) { if (row < 0 || row >= rows || col < 0 || col >= cols || revealed[row][col]) { return; } revealed[row][col] = true; if (board[row][col] == 'M') { return; } if (board[row][col] == '0') { for (auto dir : directions) { reveal(row + dir.first, col + dir.second); } } } private: void placeMines(int mineCount) { random_device rd; mt1

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值