Dijkstra+DFS-PAT1018

反思点:
  1. Dijkstra细节不能出错
  2. 深搜记录路径的写法:遍历完当前节点要temppath.pop_back().
初始思路:
/*//记录最短路径
//距离:边权,节点上:点权。

//错误原因:根本错误————————need/back分开设置。
                    //——————最小need/back在路径上的传递不遵循最优子结构!!!
//易错:节点:0 - n-1
#include<bits/stdc++.h>
#define inf 0x3fffffff
using namespace std;

int num[MAXN], sp, n;
bool vis[MAXN] = {false};
int w[MAXN], d[MAXN];  //最小拿自行车数目,最短距离
int G[MAXN][MAXN],  weight[MAXN]; //记录边权,点权

//错因:求最短和记录边权点权发生了错误。————思路混乱!!!

vector<int> path;

void Dijkstra(int s){
    path.push_back(s);
    for(int i = 1; i <= n; i++){
        int u = -1;
        for(int j = 1; j <= n; j++){
            int min_d = inf;
            if(vis[i] == false && min_d > d[i]){
                u = i;
                min_d = u;
            }
        }
        vis[u] = true;
        path.push_back(u);

        if(u == sp) return;
        for(int i = 1; i <= n; i++){
            if(vis[i] == false && G[u][i]){
                if(d[u]+G[u][i]<d[i]){
                    d[i] = d[u]+G[u][i];
                    num[i] = num[u];
                    w[i] = w[u] + weight[i];
                }else if(d[u]+G[u][i]==d[i]){
                    num[i] += num[u];
                    if(w[i] > weight[i] + w[u]){
                        w[i] = weight[i] + w[u];
                    }
                }

            }
        }
    }
}

int main(){
    int cmax, m;
    cin >> cmax >> n >> sp >> m;
    for(int i = 1;i  <= n; i++){
        cin >> weight[i];
        weight[i] = cmax / 2 - w[i];
    }
    int n1, n2, weight;
    for(int i = 0; i < m; i++){
        cin >> n1 >> n2 >> weight;
        G[n1][n2] = G[n2][n1] = weight;
    }
    num[0] = 1;
    fill(d, d+MAXN, inf);
    d[0] = 0;
    Dijkstra(0);

    cout << num[sp]<<" ";
    for(int i = 0; i < path.size(); i++){
        if(!i) cout << path[i];
        else cout << "->" << path[i];
    }
    cout << " " << w[sp] << endl;

    return 0;
}
*/
整理思路:———认识自己思路上的错误!
//整理思路:首先清楚题意:带去为need, 返回为back
//          dijkstra找到最短路径,
//          dfs  由终点逆推得  最小need/back:因为在所有的符合最短的最短路径中,
//                                      只有你逆推回去,才知道起初需要多少
//                                      从开始传递也可以吧???————不可以!



#include<bits/stdc++.h>
#define MAXN 505
using namespace std;
int G[MAXN][MAXN];
int weight[MAXN], d[MAXN],/* num[MAXN],*/ sp, n;
bool vis[MAXN];
vector<int> pre[MAXN];
vector<int> path, temppath;
const int inf = 0x3fffffff;

int minneed = inf;
int minback = inf; //初始化别丢。

void DFS(int now_vis){
    temppath.push_back(now_vis);    //记录路径
    if(now_vis == 0){
        int need = 0, back_ = 0;
        //错误原因1:temppath从0-size()-1 记录的是逆推路线
        //所以要从起点开始,记录当前的need, back!!!
        for(int i =  temppath.size() - 1 ; i >= 0; i--){
            int now = temppath[i];

        //错误原因2:need,back同步进行——————从起点开始第
        if(weight[now] > 0){   //多久取走,少再讨论是从刚开始取的,还是从前面取出的补上的
                               //注意逻辑的先后顺序
           back_ += weight[now];
        }else{//weight<=0
            if(back_ > (0-weight[now])){
                back_ += weight[now];
            }else{
               need += ((0-weight[now])-back_);
               back_ = 0;
            }
        }
        }

        //更新
        if(minneed > need){
            path = temppath;
            minneed = need;
            minback  = back_;
        }else if(need == minneed && minback > back_){
            path = temppath;
            minback = back_;
        }
        temppath.pop_back();   //还是pop抵达的最后一个节点0, 不影响其他深搜分支上的路径的输入
        return ;
    }
    for(int i = 0; i < pre[now_vis].size(); i++)
        DFS(pre[now_vis][i]);
    temppath.pop_back();  //DFS跳出当前路径包含此节点——————不影响其他深搜分支上的节点的的录入
}

int main(){
    int m, cmax;
    cin >> cmax >> n >> sp >> m;
    for(int i = 1; i <= n; i++){
        cin >>  weight[i];
        weight[i] = weight[i] - cmax/2;  //负说明need, 正说明取走back
    }
    fill(d, d+MAXN, inf);
    fill(G[0], G[0]+MAXN*MAXN, inf);
    d[0] = 0;
    fill(vis, vis+MAXN, false);
    for(int i = 0; i < m; i++){
        int n1, n2, w;
        cin >> n1 >> n2 >> w;
        G[n1][n2] = G[n2][n1] = w;
    }

    //此题相当于从0开始,到达n

    for(int i = 0; i <= n; i++){
        int u = -1, min_d = inf;
        for(int j = 0; j <= n; j++){
            if(vis[j] == false && d[j] < min_d){
                min_d = d[j];
                u = j;
            }
        }
        if(u == -1) break;
        //cout << "u :" << u << endl;
        vis[u] = true;

        for(int j = 0; j <= n; j++){
            if(vis[j] == false && G[u][j]!=inf){
                //cout << "j: " << j << endl;
                 if(d[j] > d[u] + G[u][j]){
                    d[j] = d[u] + G[u][j];
                    pre[j].clear();
                    pre[j].push_back(u);
                 }else if(d[j] == d[u] + G[u][j]){
                    pre[j].push_back(u);
                 }
            }
        }
    }
    /*
     d[0] = 0;    for(int i = 0; i <= n; i++) {
         int u = -1, min_d = inf;
         for(int j = 0; j <= n; j++) {
                if(vis[j] == false && d[j] < min_d) {
                    u = j;
         min_d = d[j];            }        }
         if(u == -1) break;
         vis[u] = true;
         for(int v = 0; v <= n; v++) {
           if(vis[v] == false && G[u][v] != inf) {
                if(d[v] > d[u] + G[u][v]) {
                    d[v] = d[u] + G[u][v];
           pre[v].clear();
pre[v].push_back(u);
}else if(d[v] == d[u] + G[u][v]) {
    pre[v].push_back(u);
    }
    }

    }
}*/
    DFS(sp);

    printf("%d ", minneed);
    for(int i = path.size()-1; i >= 0; i--){     //-2  不是-1:0已与minneed写入时
        if(i == path.size()-1) cout << path[i];
        else cout << "->" << path[i];
    }
    printf(" %d", minback);

    return 0;
}
真正整理思路:

//Dijstra算法出现了bug
//找bug给我找了一个多小时,气死了,原来是vis[j]==false 写成了vis[u] == false

///所以现在问题又回到了,把所有的最短路径存好后,再深搜,统计整条路径上的找到need(back)最小的情况
///此问题是从开始到结束节点,整个过程求最小需要的need(back),而不是累加求最大和或者最小和,所以得考虑前后所有整体的正负情况,暴力枚举(深搜)所有情况找出最符合要求的情况,而不是最优子结构!!!——————get!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值