1 条题解

  • 0

    如果考虑DP的话,那么应该如何划分状态?
    首先需要确定我们要考虑的因素,因为Hamiton路径每个点只能经过一次,并且要经过所有的点,所以我们应该考虑当前的状态经过哪些点,但是如果只考虑当前经过哪些点的话,我们没有办法确定当前的最短路径是多少,即我们不知道我们是从哪个点转移过来的,那么我们就需要考虑我们当前所在的是哪个点,那么我们就可以知道我们从哪里转移过来,边权是多少,我们就可以更新我们的最短路。

    我们需要考虑的因素:

    1.当前经过哪些点

    2.当前在那哪个点

    那么我们就应该考虑状态:

    我们用f[i][j]f[i][j]表示当前的状态,其中i是当前所经过点的集合,j是当前所在的点。

    考虑完状态之后我们就需要考虑如何转移:

    因为每两个点之间都存在路径,所以我们可以从任意一个点转移过来,但是我们要确保向当前状态转移的状态的当前状态点没有访问且访问了向当前状态转移的状态的点。

    即:

    f[i][j]=min([statek][k]+w[k][j],f[i][j])f[i][j] = min([state_k][k]+w[k][j],f[i][j])

    其中statekstate_k jj未被访问,且包括kk这个点。

    换言之就是statekstate_k为集合iijj未被访问,但kk被访问的集合,这样就与当前状态建立了联系。

    那么我们首先可以枚举集合ii,即枚举通过的点来扩展状态。

    接下来我们可以通过枚举合法的jj与合法的kk来确定状态的最优解。

    jj合法:即ii集合中包括jj

    kk合法:即ii集合中kk已被访问。

    那么我们用什么来确定每个点是否被访问?

    因为数据规模比较小,所以我们可以用一个32位二进制整数n的第i位来保存第i个点是否被访问。


    那么我们就可以得到完整的状态转移方程:

    f[i][j]=min(f[ixor(1<<j)][k]+w[k][j],f[i][j])f[i][j] = min(f[i xor(1<<j)][k] + w[k][j],f[i][j])

    且满足于(i>>j)(i >> j) & 1=1 1 = 1

    (ixor(1<<j))>>k(i xor(1<<j)) >> k & 1=11 = 1


    其中,(i>>j)(i >> j) & 1=1 1 = 1(是确保当前状态下i的第jj位一定为1,即jj点被访问。

    ixor(1<<j)i xor(1<<j)则代表将ii的第jj位取反 即在iijj未被访问(其余不变)的状态。

    其复杂度为O(2nn)O(2^n*n)

    #include <iostream>
    #include <cstring>
    using namespace std;
    int f[1 << 20][25],w[25][25],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&(1<<j) == 1 << j){
                    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;
    }
    
  • 1

信息

ID
1002
难度
9
分类
(无)
标签
(无)
递交数
1
已通过
1
通过率
100%
上传者