PAT1018

题目

在一个公共自行车系统中,有多个自行车点和连接他们的路,现在给出一点V,要求求出从公共自行车调度中心到达V最短的路径,如果不唯一,就找出需要带出最少自行车数量的路径,如果仍然不唯一,就找出最少带回自行车数量的路径。

错误的操作

一开始,只注意到了一条路径上的问题,并未考虑到这个题不能单纯的用Dijskra算法,只用Dijkstra算法可以过前两个点,有一半分(可能就是送的。。。这题的核心还是在后面)

void Dijkstra(int s) {
	fill(d, d + MAX_V, INF);
	d[s] = 0;
	S[s] = store[s];
	for (int i = 0; i < N; i++) {
		int min = INF, u = -1;
		for (int j = 0; j < N; j++) {
			if (min > d[j] && visit[j] == false) {
				u = j;
				min = d[j];
			}
		}
		if (u == -1) return;
		visit[u] = true;
		for (int j = 0; j < Adj[u].size(); j++) {
			int v = Adj[u][j].v;
			if (visit[v] == false) {
				if (d[u] + Adj[u][j].dis < d[v]) {
					d[v] = d[u] + Adj[u][j].dis;
					S[v] = S[u] + store[v];
					pre[v] = u;
				}
				else if (d[v] == d[u] + Adj[u][j].dis && S[u] + store[v] > S[v]) {
					S[v] = S[u] + store[v];
					pre[v] = u;
				}
			}
		}
	}
}

代码和思路

  1. 这个题目,有三个判断条件,条件不能累计,不具有最优子结构,不能直接使用Dijkstra算法。
  2. 不能使用直接使用Dijkstra算法,就需要将所有的最短路先记录下来,使用pre数组记录前驱,回溯可以记录所有的最短路了。回溯的过程就是DFS,在下面的代码中会给每一步的具体解释。

对代码每部分的详细解释

  1. 对Dijkstra的优化
    1. 当最短路径可以更新的时候,则之前的所有点距离一定是远的,可以直接清空,把这个中介点u压进去
    2. 如果最短路径相同,则把中介点u直接跟在pre[]数组里。
    void Dijsktra(int s) {
    fill(d, d + MAXV, INF);
    d[s] = 0;
    for (int i = 0; i < n; i++) {
    	int u = -1, MIN = INF;
    	for (int j = 0; j < n; j++) {
    		if (vis[j] == false && d[j] < MIN) {
    		    u = j;
    			MIN = d[j];
    		}
    	}
    	if (u == -1) return;  //和s不连通
    	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];
    				//u是当前唯一最短路,所以要更新v的前驱,之前的全部清空,然后把u压进去
    				pre[v].clear();
    				pre[v].push_back(u);
    			}
    			else if(d[v] == d[u] + G[u][v]) { //u不是当前唯一最短路,直接u压进数组
    				pre[v].push_back(u);
    			}
    		}
    	}
    }
    

}

  1. DFS

    伪代码

    这就是个深搜的过程,先把当前结点进数组,然后递归,直到搜到了根节点0,然后计算这条路径上的数据,计算完毕后,把路径上当前最后一个点弹出(就是一个恢复的过程)。
    for循环结束后,证明这个v节点的后续已经结束了,pop这个v节点(恢复)。
    DFS(){
    	if(v == 0){
    		递归出口, 从后往前遍历,把需要送出的车和需要拿回的车记录下来。
    		比较更新最终值
    		弹出
    		return
    	}
    	v进数组
    	for(v的所有前驱){
    		DFS(前驱)
    	}
    	把数组最后一个值弹出
    }
    

    具体实现

    void DFS(int v) {
    if (v == 0) {
    //出口,其实就是深搜完这条最短路径了,可以进行计算。
    	tempPath.push_back(v);
    	int need = 0, remain = 0;
    	for (int i = tempPath.size() - 1; i >= 0; i--) {
    		int id = tempPath[i];
    		if (weight[id] > 0) {
    			remain += weight[id];
    		}
    		else {
    			if (remain > abs(weight[id])) {
    				remain -= abs(weight[id]);
    			}
    			else {
    				need += abs(weight[id]) - remain;
    				remain = 0;
    			}
    		}
    	}
    	if (need < minNeed) {
    		minNeed = need;
    		minRemain = remain;
    		path = tempPath;
    	}
    	else if(need == minNeed && remain < minRemain){
    		minNeed = need;
    		minRemain = remain;
    		path = tempPath;
    	}
    	//这条路径已经读完,回到根节点前一个点继续递归深入
    	tempPath.pop_back();
    	return;
    }
    tempPath.push_back(v);
    for (int i = 0; i < pre[v].size(); i++) {
    	DFS(pre[v][i]);
    }
    //回溯结束,已经回到了v,把v弹出
    tempPath.pop_back();
    }
    

完整AC代码

#include<cstdio>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int MAXV = 510;
const int INF = 100000;

//n为顶点数, m为边数, Cmax为车站最大承载量, sp为问题站
//weight为点权,使用邻接矩阵
//minNeed记录最少携带数目,minRemain记录最少带回数目
int n, m, Cmax, Sp, numPath = 0, G[MAXV][MAXV], weight[MAXV];
int d[MAXV], minNeed = INF, minRemain = INF;
bool vis[MAXV] = { false };
vector<int> pre[MAXV];      //记录每个节点在最短路情况下的前驱(有可能有多个)
vector<int> tempPath, path;  //临时路径和最优路径

void Dijsktra(int s) {
	fill(d, d + MAXV, INF);
	d[s] = 0;
	for (int i = 0; i < n; i++) {
		int u = -1, MIN = INF;
		for (int j = 0; j < n; j++) {
			if (vis[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;  //和s不连通
		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];
					//u是当前唯一最短路,所以要更新v的前驱,之前的全部清空,然后把u压进去
					pre[v].clear();
					pre[v].push_back(u);
				}
				else if(d[v] == d[u] + G[u][v]) { //u不是当前唯一最短路,直接u压进数组
					pre[v].push_back(u);
				}
			}
		}
	}
}

void DFS(int v) {
	if (v == 0) {
		tempPath.push_back(v);
		int need = 0, remain = 0;
		for (int i = tempPath.size() - 1; i >= 0; i--) {
			int id = tempPath[i];
			if (weight[id] > 0) {
				remain += weight[id];
			}
			else {
				if (remain > abs(weight[id])) {
					remain -= abs(weight[id]);
				}
				else {
					need += abs(weight[id]) - remain;
					remain = 0;
				}
			}
		}
		if (need < minNeed) {
			minNeed = need;
			minRemain = remain;
			path = tempPath;
		}
		else if(need == minNeed && remain < minRemain){
			minNeed = need;
			minRemain = remain;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(v);
	for (int i = 0; i < pre[v].size(); i++) {
		DFS(pre[v][i]);
	}
	tempPath.pop_back();
}

int main() {
	scanf_s("%d%d%d%d", &Cmax, &n, &Sp, &m);
	int u, v;
	fill(G[0], G[0] + MAXV * MAXV, INF);
	for (int i = 1; i <= n; i++) {
		scanf_s("%d", &weight[i]);
		weight[i] -= Cmax / 2;
	}
	for (int i = 0; i < m; i++) {
		scanf_s("%d%d", &u, &v);
		scanf_s("%d", &G[u][v]);
		G[v][u] = G[u][v];
	}
	Dijsktra(0);
	DFS(Sp);
	printf("%d ", minNeed);
	for (int i = path.size() - 1; i >= 0; i--) {
		printf("%d", path[i]);
		if (i > 0) printf("->");
	}
	printf(" %d", minRemain);
}

总结

这是一个Dijkstra + DFS的过程
主要学到了如何用pre二维数组进行深搜最短路。
最短路pre数组最后其实就是一个确定了首尾的树

代码来自于算法笔记,对于代码的具体理解是自己debug一步步得来的。可能有所错误和误解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
function copy-data([int]$startrow, [int]$startcol, [int]$endrow, [int]$endcol, [string]$pat, [string]$file, [int]$sheetfrom, [int]$sheetto) { $excel = New-Object -ComObject Excel.Application $excel.Visible = $false $wb = $excel.Workbooks.Open($file) $wsfrom = $wb.Sheets.Item($sheetfrom) $wsto = $wb.Sheets.Item($sheetto) $sr = $wsfrom.Range($wsfrom.Cells.Item($startrow, $startcol), $wsfrom.Cells.Item($endrow, $endcol)) $fc = $sr.Find($pat) if ($fc -ne $null) { $rown = $fc.Row $dsr = $wsto.Range($wsto.Cells.Item($rown, $startcol), $wsto.Cells.Item($rown + ($endrow - $startrow), $endcol)) $dsr = $dsr.Resize($sr.Rows.Count, $sr.Columns.Count) $sr.Copy() $dsr.PasteSpecial(-4104) } $excel.DisplayAlerts = $false $wb.Close($true) [Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null } $path = $PWD.Path $file = "$path\a.xlsx" # 将sheet1中第1行到第4行、第2列到第9列的数据复制到sheet2中 copy-data -startrow 1 -startcol 2 -endrow 4 -endcol 9 -pat 1009 -file $file -sheetfrom 1 -sheetto 2 # 将sheet1中第6行到第8行、第2列到第9列的数据复制到sheet2中 copy-data -startrow 6 -startcol 2 -endrow 8 -endcol 9 -pat 1018 -file $file -sheetfrom 1 -sheetto 2 # 将sheet1中第10行到第11行、第2列到第9列的数据复制到sheet2中 copy-data -startrow 10 -startcol 2 -endrow 11 -endcol 9 -pat 1023 -file $file -sheetfrom 1 -sheetto 2 这个是搜索sheet1中关键词吗,我现在要搜索sheet2中关键词 得到行信息后再把sheet1指定内容复制到sheet2指定区域,怎么修改呢
05-19

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值