有N个城市。每个城市都有1到10(含)的传送。您将获得一个大小为N的数组,表示每个城市的整数传送类型。对于前者1 3 5 3 8 9
。目标是根据以下规则找到从数组中的第一个条目到最后一个条目所需的最小小时数:
3
。此操作需要花费给定的R小时数。我已经实现了一个动态编程解决方案,当最快的方式向前发展时,它可以完美地运行。但是对于某些情况来说,回到几个城市再向后传送更快,从而最大限度地减少花费的时间。
这是我的算法失败的一个例子:ex。 2 1 3 4 6 8 5 7 4 2 3 5 2 1 3 4 3 5 6 7
和R = 2
正确答案:索引0(时间= 0) - > index 9(时间= 2) - >索引8(时间= 3) - > index 7(time = 4) - >指数19(时间= 6)。
我的算法会发现只有向前移动的最快方式,当显然,正确的最快方式也包括向后移动。
到目前为止,这是我的代码:
#include <iostream>
using namespace std;
int main()
{
int dp[200] = {};
short ti[200] = {};
int numCities, R;
cin >> numCities >> R;
for (int i = 0; i < numCities; i++)
{
short temp;
cin >> temp;
ti[i] = temp;
}
for (int i = 1; i < numCities; i++)
{
dp[i] = dp[i - 1] + 1;
for (int x = i - 1; x >= 0; x--)
{
if (ti[x] == ti[i])
{
if (R + dp[x] < dp[i])
dp[i] = R + dp[x];
}
}
}
cout << dp[numCities - 1];
return 0;
}
如何使我的算法适用于这种情况,其中较低的状态取决于较大的状态?
编辑:我使用以下方式进行动态编程:对于每个城市,我计算了在起始状态dp[0] = 0
下到达它们的最快方式。
然后重现关系是:dp[i] = min(dp[i - 1] + 1, dp[every other point with same teleport type] + R)
答案 0 :(得分:4)
动态编程适用于问题具有最佳子结构的情况。也就是说,您必须找到一种方法来细分问题,以便将细分的最佳解决方案用作构建块,以找到解决整个问题的最佳解决方案。
上面,我看到你说你想要使用动态编程。我看到了代码。我看不到的是你正在考虑哪些子问题的明确解释。也就是说:对解决方案的概念性理解是获得正确动态编程的关键,这正是您未提供的。
我的直觉是动态编程不是这种情况下的好方法,因为:
您可以使用嵌套循环来解决这些问题。这为您提供了 O(n ^ 2)时间解决方案。
但是,将此问题视为加权图遍历的一个实例,您可以使用 O(n log n + m)时间内的Dijkstra's algorithm来解决此问题( O( n)遍历足以建立每个节点的邻居)其中 m 是所考虑的边数(这里可以将其限制为Θ(m)的值< / em>通过识别每个传送器类型仅使用一次)。为什么不这样做?
您可以尝试使用A*来改善运行时间,但我不相信这会在一个维度上提供很多改进。
完成此操作的代码可能如下所示:
#include <iostream>
#include <queue>
#include <unordered_set>
#include <unordered_map>
typedef std::unordered_map<int, std::vector<int> > tele_network_t;
int Dijkstra(const std::vector<int> &graph, const tele_network_t &tn, const int R){
//This whole mess makes the smallest elements pop off the priority queue first
std::priority_queue<
std::pair<int, int>,
std::vector< std::pair<int, int> >,
std::greater< std::pair<int, int> >
> pq; //<distance, index>
//Keeping track of the teleporters used allows us to speed up the algorithm by
//making use of the theorem that each teleporter type will be used only once.
std::unordered_set<int> teleporters_used;
//Keep track of the path
std::vector<int> parent(graph.size(),-1); //Parent==-1 indicates an unvisited node
//At 0 distance, place the 0th node
pq.emplace(0,0);
parent[0] = 0; //The only node whose parent is itself should be node 0
while(!pq.empty()){
const auto c = pq.top();
pq.pop();
//We've reached the goal node
if(c.second==graph.size()-1){
std::cout<<"Dist = "<<c.first<<std::endl;
break;
}
//Insert neighbours
if(c.second!=0 && parent[c.second-1]==-1){ //Left neighbour
parent[c.second-1] = c.second;
pq.emplace(c.first+1,c.second-1);
}
if(parent[c.second+1]==-1){ //Right neighbour: can't overflow because goal is the rightmost node
parent[c.second+1] = c.second;
pq.emplace(c.first+1,c.second+1);
}
//Inner loop is executed once per teleporter type
if(teleporters_used.count(graph[c.second])==0)
for(const auto i: tn.at(graph[c.second])){
if(parent[i]==-1){
pq.emplace(c.first+R,i);
parent[i] = c.second;
}
}
teleporters_used.insert(graph[c.second]);
}
//Trace our steps backwards to recover the path. Path will be reversed, but a
//stack could be used to fit this.
int p = graph.size()-1;
while(parent[p]!=p){
std::cout<<p<<std::endl;
p = parent[p];
}
std::cout<<0<<std::endl;
}
int main(){
tele_network_t tele_network;
const int R = 2;
std::vector<int> graph = {{2,1,3,4,6,8,5,7,4,2,3,5,2,1,3,4,3,5,6,7}};
//Determine network of teleporters
for(int i=0;i<graph.size();i++)
tele_network[graph[i]].push_back(i);
Dijkstra(graph, tele_network, 2);
}