反思点:
- Dijkstra细节不能出错
- 深搜记录路径的写法:遍历完当前节点要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!