1. 程式人生 > 實用技巧 >最短Hamilton路徑(狀態壓縮DP)

最短Hamilton路徑(狀態壓縮DP)

分析:

     首先我們要思考如果讓這個NP完全題目複雜度降低,那麼可以優先考慮到使用位運算,狀態壓縮等解決思路。

    然後接著思考,我們可以發現,我們所需要的不是整個方案,而只是方案最優解,所以我們只需要記錄當前這個方案的最優解即可,那麼我們考慮的狀態,不久只有,在當前方案i中,目前抵達的點是j。
    現在既然裝填已經確定好了當前點j,那麼這個j點是由哪一個狀態移動而來的呢?我們可以選擇k,也就是說我們的狀態轉移方程可以為
      f[i][j]=min(f[i][j],f[i^(1<<j)][k]+w[k][j]

    以上轉移方程,w陣列為權值 ,也就是w[k][j]是k點到j點的權值

    i^(1<<j)的意思是,i 異或 1右移j位,具體來說就是i這個方案集合 xor 10……0,(其中1的位置在第j位)。
    那麼這個位運算有什麼用處呢,第一點它是在判斷第j位的情況,第二點位運算處理速度很快。

import java.util.*;
class Main {
    
    public static void main(String[] args) {
        
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        
int[][] w = new int[n][n]; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { w[i][j] = sc.nextInt(); } } int[][] f = new int[1<<n][n]; for(int[] d : f) Arrays.fill(d,Integer.MAX_VALUE / 2); f[1][0] = 0;
for(int i = 0; i < 1 << n; i++) { for(int j = 0; j < n; j++) { if(((i >> j) & 1) != 0) { for(int k = 0; k < n; k++) { if((((i - (1 << j)) >> k) & 1) != 0) { f[i][j] = Math.min(f[i][j],f[i-(1<<j)][k] + w[k][j]); } } } } } System.out.print(f[(1<<n)-1][n-1]); } }
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 20, M = 1 << N;

int n;
int f[M][N];
int w[N][N];


int main() {
    cin >> n;
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            cin >> w[i][j];
        }
    }
    memset(f,0x3f,sizeof f);
    f[1][0] = 0;
    for(int i = 0; i < 1 << n; i++) {
        for(int j = 0; j < n; j++) {
            if(i>>j&1) {
                for(int k = 0; k < n; k++) {
                    if((i-(1 << j)) >> k & 1) {
                        f[i][j] = min(f[i][j],f[i-(1 << j)][k] + w[k][j]);
                    }
                }
            }
        }
    }
    cout << f[(1 << n) - 1][n-1];
    return 0;
}