1. 程式人生 > 其它 >Android連線阿里雲(二)

Android連線阿里雲(二)

題目連結:https://www.acwing.com/problem/content/93/

題目描述

給定一張 n 個點的帶權無向圖,點從 0∼n−1 標號,求起點 0 到終點 n−1 的最短 Hamilton 路徑。
Hamilton 路徑的定義是從 0 到 n−1 不重不漏地經過每個點恰好一次。

輸入描述

第一行輸入整數 n。
接下來 n 行每行 n 個整數,其中第 i 行第 j 個整數表示點 i 到 j 的距離(記為 a[i,j])。
對於任意的 x,y,z,資料保證 a[x,x]=0,a[x,y]=a[y,x] 並且 a[x,y]+a[y,z]≥a[x,z]。

輸出描述

輸出一個整數,表示最短 Hamilton 路徑的長度。
1≤n≤20
0≤a[i,j]≤10^7

示例

輸入

5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0

輸出

18

分析

約定

記從i到j的距離為s[i][j]

狀態表示

f(i,j)表示滿足如下條件行走方案的最小路徑長度:

  • i為狀態量,表示行走時經過的點
  • 從0點出發到達j

將i看作一個二進位制數,第k位不為0表示途徑k點,為0表示不經過k點。

狀態劃分與計算

對於f(i,j),我們還是按照以往的思路,試著將達成f(i,j)的最後一步操作進行劃分。
對應本題中,我們要關注的就是整個路徑中的倒數第二個點,通過對倒數第二個點是誰進行分類。
狀態轉移方程如下:
f(i,j)=min(f(i-(1<<j),k)+s[k][j])
還有一些處理詳見程式碼。

邊界處理

f(1,0)=0,其餘都是正無窮。

AC程式碼


#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>

using namespace std;

const int N = 20, M = 1 << N;
int n;
int f[M][N];
int s[N][N];

int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> s[i][j];

	memset(f, 0x3f, sizeof(f));
	f[1][0] = 0;
	for (int i = 0; i < 1 << n; i++)
		for (int j = 1; j < n; j++)
			if (i >> j & 1)		//目的地是j 那i中j對應的那一位得是1 否則沒有必要繼續討論
				for (int k = 0; k < n; k++)
					if ((i - (1 << j)) >> k & 1)  //倒數第二個點對應的位也得是1
						f[i][j] = min(f[i][j], f[i - (1 << j)][k] + s[k][j]);

	//最終解果是經過所有點到達n-1
	cout << f[(1 << n) - 1][n - 1] << endl;
	return 0;
}