我正在对TSP实施贪婪方法:
1.从第一个节点开始。
1.转到最近的尚未访问的节点。(如果是多个,则转到索引最小的节点。)
1.不要忘记包括从节点1到最后访问的节点的距离。
然而,我的代码给出了错误的答案。我在Python中实现了同样的代码,Python代码给出了正确的答案。
在我的问题中,节点是二维平面上的坐标,距离是欧几里得距离。
我甚至将所有内容都改为long double
,因为它更精确。
事实上,如果我颠倒for
循环的顺序来反转方向,并添加一个额外的if
语句来处理关系(我们希望最近节点的索引最小),它会给出一个非常不同的答案。
这是因为精度问题吗?
(Note:我必须打印floor(ans)
)
输入:Link
预期输出:1203406
实际输出:1200403
#include <iostream>
#include <cmath>
#include <vector>
#include <cassert>
#include <functional>
using namespace std;
int main() {
freopen("input.txt", "r", stdin);
int n;
cin >> n;
vector<pair<long double, long double>> points(n);
for (int i = 0; i < n; ++i) {
int x;
cin >> x;
assert(x == i + 1);
cin >> points[i].first >> points[i].second;
}
// Returns the squared Euclidean Distance
function<long double(int, int)> dis = [&](int x, int y) {
long double ans = (points[x].first - points[y].first) * (points[x].first - points[y].first);
ans += (points[x].second - points[y].second) * (points[x].second - points[y].second);
return ans;
};
long double ans = 0;
int last = 0;
int cnt = n - 1;
vector<int> taken(n, 0);
taken[0] = 1;
while (cnt > 0) {
pair<long double, int> mn = {1e18, 1e9};
for (int i = 0; i < n; ++i) {
if (!taken[i]) {
mn = min(mn, {dis(i, last), i});
}
}
int nex = mn.second;
taken[nex] = 1;
cnt--;
ans += sqrt(mn.first);
last = nex;
}
ans += sqrt(dis(0, last));
cout << ans << '\n';
return 0;
}
UPD:Python代码:
import math
file = open("input.txt", "r")
n = int(file.readline())
a = []
for i in range(n):
data = file.readline().split(" ")
a.append([float(data[1]), float(data[2])])
for c in a:
print(c)
def dis(x, y):
cur_ans = (a[x][0] - a[y][0]) * (a[x][0] - a[y][0])
cur_ans += (a[x][1] - a[y][1]) * (a[x][1] - a[y][1])
cur_ans = math.sqrt(cur_ans)
return cur_ans
ans = 0.0
last = 0
cnt = n - 1
take = []
for i in range(n):
take.append(0)
take[0] = 1
while cnt > 0:
idx = -1
cur_dis = 1e18
for i in range(n):
if take[i] == 0:
if dis(i, last) < cur_dis:
cur_dis = dis(i, last)
idx = i
assert(idx != -1)
take[idx] = 1
cnt -= 1
ans += cur_dis
last = idx
ans += dis(0, last)
print(ans)
file.close()
# 1203406
1条答案
按热度按时间uhry853o1#
是的,差异是由于舍入误差造成的,由于使用了
long double
,C代码产生的结果更精确。如果更改C代码,使其使用与Python * 相同的精度(IEEE-754,表示double
精度)* 在这两种代码中,您会得到完全相同的舍入误差。把你的例子归结为4000点:https://godbolt.org/z/rddrdT54n如果我在整个输入文件上运行相同的代码,我会在C和Python中得到1203406.5012708856 (不得不离线尝试,因为Godbolt很容易杀死进程)。
请注意,理论上Python代码和C代码并不完全相似,因为
std::min
会按字典顺序比较元组和对。因此,如果有两个距离完全相等,std::min
调用将选择两个索引中较小的一个。但实际上,这并没有什么区别。现在我不认为你真的可以消除舍入误差。有一些技巧可以最小化它们。
long double
)是一种选择。但是这也会使你的代码变慢,这是一种折衷第一个
最后一点意见,与你的问题有点无关:将
auto
与lambda一起使用,即C++有许多不同种类的可调用对象 (functions,function pointer,functors,lambdas,...) 和
std::function
是一个有用的 Package 器,它可以用一个类型来表示具有相同签名的所有类型的可调用对象。(运行时多态性,所以如果你不需要std::function
的类型擦除功能,只需将lambda存储在用auto
声明变量中。