SS241112A. 定向越野(walk)
SS241112A. 定向越野(walk)
题意
给你 \(n\) 个点,\(n \le 12\),你可以从任意一个点出发以任意顺序依次遍历所有点燃火回到起点,你只能拐直角走,问最小路程。答案输出最小路程的平方,输出分数形式。可以证明最小路程的平方一定是有理数。
思路
显然枚举遍历顺序。
首先需要明白为什么答案一定是有理数。
感觉我们的初始方向必须是某一个向量的方向,假设这个是正确的。
考虑起点初始方向是 \(\alpha\),我们可以把整个图旋转 \(\alpha\),这样相当于我们只能往坐标轴方向走。
考虑旋转后每个点的坐标,起点是 \((0,0)\),第一个经过的点是 \((len_{0,1},0)\)(其中 \(len_{u,v}\) 表示点 \(u\) 和点 \(v\) 的直线距离)。每个点与原点连线,相当于这条线的角度减去 \(\alpha\)。设这条线的角度是 \(\beta\),\(\alpha\) 和 \(\beta\) 的 \(\tan\) 都是有理数,已知。那么就可以求出 \(\beta-\alpha\) 的 \(\tan\) 了,也是有理数。
不记得三角函数公式……
我们要证明答案只含一种无理数,否则如果是多个无理数相加,平方后仍然是无理数。
也就是证明答案仅含有 \(len(1,2)\) 一种或零种无理数。
点 \(u\) 的坐标 \(x_u=\cos x \cdot len_{1,u},y_u=\sin x \cdot len(1,u)\),展开后因为 \(\sin x,\cos x\) 分母里本身有一个 \(len_{1,u}\),因此就消掉了 \(len_{1,u}\),只剩下一个有理数乘(其实是除啦) \(len(1,2)\) 了。
证毕。
回到最初的假设,事实上这个结论是正确的。我不会证明。想象一个等腰直角三角形,最优的路径是以底边为初始方向,你发现虽然你微调初始方向后上边底边的路变长,上面的路变短,但是这是不优的。因此大胆猜测结论正确?
关键是如果没有这个结论,你没法做啊。所以结论是对的。。。
题解写得挺好。
在计算器上画出 \(y=\sin x + \cos x\) 函数图像。
长这样。其实应该是 \(y=\sin x + \cos x\):
这是个分段的凸函数,当 \(x=0\) 的时候取得最小值。所以它们加起来的最小值一定是在某个 \(x=0\) 的位置取到,因为如果你取其他位置,一定可以选择旁边的位置使答案更小。
我觉得很难理解。容易想错。只是给出结论再去证明是相对好感性理解的。
据 @Hagasei 大佬说,如果你不会证明这步,可以观察发现大样例的分母都是某个 \((x_i-x_j)^2+(y_i-y_j)^2\),因此认为结论正确。
嗯没了。
直接枚举遍历顺序是阶乘的,可以状压 DP,状态是初始方向,哪些点已经经过,最后一个点是什么,其中初始方向可以循环数组,时间 \(O(n^3 2^n)\),空间 \(O(n 2^n)\),转移枚举下一个点,总时间是 \(O(n^4 2^n)\)。
code
参考了 dxw 的代码。
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace entirely {
constexpr int N=20,inf=0x3f3f3f3f;
int n;
struct node {
int x,y;
}a[N];
struct frac {
ll p,q;
}ans;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b) : a; }
int f[1<<13][N];
bool bit (int s,int k) { return (s>>k)&1; }
void _min(int &a,int b) { a=min(a,b); }
int dis[N][N];
void main() {
sf("%d",&n);
rep(i,1,n) sf("%d%d",&a[i].x,&a[i].y);
if(n==1) { puts("0 1"); return; }
ans={1,0};
rep(i,1,n) rep(j,i+1,n) {
int dxa=a[j].x-a[i].x,dya=a[j].y-a[i].y;
rep(ii,1,n) rep(jj,ii+1,n) {
int dxb=a[jj].x-a[ii].x,dyb=a[jj].y-a[ii].y;
dis[ii][jj]=dis[jj][ii]=abs(dxa*dxb+dya*dyb)+abs(dxa*dyb-dya*dxb);
}
memset(f,0x3f,sizeof(f));
f[1][1]=0;
rep(s,1,(1<<n)-1) rep(x,1,n) {
if(bit(s,x-1)&&f[s][x]!=inf) {
rep(k,1,n) if(!bit(s,k-1)) {
_min(f[s|(1<<(k-1))][k],f[s][x]+dis[x][k]);
}
}
}
int y=inf,s=(1<<n)-1;
rep(k,1,n) if(f[s][k]!=inf) _min(y,f[s][k]+dis[k][1]);
frac tmp={1ll*y*y,dxa*dxa+dya*dya};
if((__int128)ans.p*tmp.q > (__int128)ans.q*tmp.p) ans=tmp;
}
ll g=gcd(ans.p,ans.q);
pf("%lld %lld\n",ans.p/g,ans.q/g);
}
}
int main() {
#ifdef LOCAL
freopen("my.out","w",stdout);
#else
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
#endif
entirely :: main();
}
本文来自博客园,作者:liyixin,转载请注明原文链接:https://www.cnblogs.com/liyixin0514/p/18542596