P9119 [春季測試 2023] 聖誕樹

superl61發表於2024-08-16

參考部落格: 春季測試 2023] 聖誕樹 題解 - 洛谷專欄 (luogu.com.cn)

題意:給定二維平面上一個凸多邊形的 \(n\) 個頂點, 求一種方案,使得從最高點開始,不重複地經過所有點後距離的最小值。只要求輸出這種方案。

經典的 TSP 問題。

\(n<=18\) 可以直接狀壓dp.

\(n<=1000\):

就是要大膽發現一些結論。

根據三角形法則,

對比圖1和圖2,發現如果電線交叉肯定不優。

所以對於逆時針給定的若干個點,當處於 \(𝑖\) 時,最優決策一定只能是下一步到 \(𝑖−1\)\(i+1\),否則將會存在一個點被隔離,導致最終去往該點時一定形成交叉路徑。並且 \(i\) 之前的決策並不影響當前這一步的決策。

此時無後效性和子問題的雛形已經出現了,考慮動態規劃。

  • 定義:\(f[i][j][0/1]\), 區間\([i,j]\), 停在左邊界/右邊界, 最短距離
  • 初始化:\(f[s][s][0/1] = 0\), 其他的初始化為 \(inf\)
  • 擴充套件順序:先從小到大列舉長度,再從左到右依次列舉所有區間
  • 答案:由\(f[1][n][1]\) 往回倒推(最後一步仍然要遵循三角形法則,所以只考慮 \(f[1][n][1].\)
#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=(r);++i)
#define G(i,r,l) for(int i(r);i>=(l);--i)
#define int ll
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int N=1e3+5;
const double inf = 1e18;
struct node{
	double x,y;
	int id;
}a[N],tmp[N];
double f[N][N][2];
int pre[N][N][2];
inline double dis(int i,int j){
	return sqrt((a[i].x-a[j].x) * (a[i].x-a[j].x) + (a[i].y-a[j].y) * (a[i].y-a[j].y));
}
int n,k=1;
inline void dfs(int i,int j,int op){
	if(i==j) return cout<<a[i].id,void();
	if(!op) cout<<a[i].id<<" ",dfs(i+1,j,pre[i][j][0]);
	else cout<<a[j].id<<" ", dfs(i,j-1,pre[i][j][1]);
	return  ;
}
signed main(){
//	freopen("tree.in","r",stdin); 
//	freopen("tree.out","w",stdout);
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin>>n; F(i,1,n) cin>>a[i].x>>a[i].y,a[i].id=i,tmp[i]=a[i];
	F(i,2,n) if(a[i].y>a[k].y) k=i;
	F(i,1,k) a[i+n-k]=tmp[i];
	F(i,k+1,n) a[i-k]=tmp[i];
	F(len,2,n-1){
		for(int i=1,j=len;j<=n;++i,++j){
			f[i][j][0] = f[i][j][1] = inf;
			if(dis(i,i+1) + f[i+1][j][0] < dis(i,j) + f[i+1][j][1]) 
				f[i][j][0] = dis(i,i+1) + f[i+1][j][0], pre[i][j][0] = 0;
			else 
				f[i][j][0] = dis(i,j) + f[i+1][j][1], pre[i][j][0] = 1;
			if(dis(j-1,j) + f[i][j-1][1] < dis(i,j) + f[i][j-1][0]) f[i][j][1] = dis(j-1,j) + f[i][j-1][1], pre[i][j][1] = 1;
			else  f[i][j][1] = dis(i,j) + f[i][j-1][0], pre[i][j][1] = 0; 
		}	
	}
	cout<<a[n].id<<" ";
	if(dis(n-1,n) + f[1][n-1][1] > dis(1,n) + f[1][n-1][0]) dfs(1,n-1,0);
	else dfs(1,n-1,1);
	return fflush(0),0;
}

總結一下,感覺拿到這道題還是沒有冷靜去分析條件,比如起點固定能不能利用?逆時針給定的這張圖的順序可能長什麼樣?不是從最高點開始給的圖,我怎麼透過一些簡單的設計去調整?有哪些情況是顯然不優的?敢不敢大膽排除?

相關文章