最短Hamilton路径与状压DP

最短Hamilton路径的意思是遍历一张图,每个节点不重不漏的走过一次(不能多),这样一种走法的最短路径。

这样的一种问题可以通过状态压缩动态规划来实现,其核心思想是以二进制形式枚举各个点是否被走过的状态,通过这些子状态推得答案。

如何设计这个方案呢?可以设DP数组第一维指代当前点的经历情况(这是二进制数字的十进制形式,比如0011就代表走过3 4两个点,在第一维以3来表现。)第二维表示当前在哪个点。

那么就有方程dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+V[k][j]),其中i^(1<<j)代表j这个点你还没有经历的情况,因为你是在模拟从k点来的情况,而j是未到达的阶段性终点。在这里用异或是肯定可以把这一位归零的,因为到枚举k之前就判断了i的第j位是否为1,如果没有这种限制条件就不一定了。

import java.io.*;
import java.util.*;
import java.math.*;
import java.text.*;
public class Main
{
    
    public static int min(int a,int b)
    {
        return b<a?b:a;
    }
    static int INF=100000008;
    public static void main(String args[])throws IOException
    {
        StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        PrintWriter pr=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
        st.nextToken();
        int n=(int)st.nval;
        int connection[][]=new int[40][40];
        int status[][]=new int[1<<n][n+5];//这个数组开太大会超时
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                st.nextToken();
                connection[i][j]=(int)st.nval;
            }
        }
        for(int i=0;i<(1<<n);i++)
            Arrays.fill(status[i],INF);
        status[1][0]=0;
        for(int i=1;i<(1<<n);i++)
        {
            for(int j=0;j<n;j++)
            {
                if(((i>>j)&1)==1)//确认j这个点是否要走
                {
                    for(int k=0;k<n;k++)
                    {
                        if(((i>>k)&1)==1)//确认k这个点是否要走
                        {
                            status[i][j]=min(status[i][j],status[i^(1<<j)][k]+connection[k][j]);
                        }
                    }
                }
            }
        }
        pr.println(status[(1<<n)-1][n-1]);
        pr.flush();
        pr.close();
    }
}

记忆化搜索与DP是互通的,并且本题并不需要时空优化,所以本题也可以用记忆化搜索来解决。

已知一个01序列可以代表你遍历图的方案,那么对于某个点来说,你可以通过分析这个序列的某一节,挑你没走过的点(如果有边)继续走下去。比如说0000111,当你位于三号点的时候,就对目前的整个01串进行分析,显然前三位都是1,这代表此点已经走过了,因为只能走一次,所以不能继续选这个点,但四五六号点就可以选取了。在选取过后,就要把目前的方案改成选取之后的样子,如上例的0000111,如果走了4号点,那么这个序列就变成0001111,可以通过加2^4实现。

如果你已经到了最后一个点,直接特判,把权值加过去就可以了。

import java.io.*;
import java.util.*;
import java.math.*;
import java.text.*;
public class Main31
{
    static int status[][]=new int[1048579][23];
    static int connection[][]=new int[40][40];
    public static int dfs(int current,int totality,int secquence,int limit)
    {
        if(totality==limit-1)
            return connection[current][limit-1];
        else if(status[secquence][current]>0)
            return status[secquence][current];
        else
        {
            int ans=0x3f3f3f3f;
            for(int i=1;i<=limit-1;i++)
            {
                if((secquence&(1<<i))==0)//拆分序列,进行分析
                    ans=Math.min(ans,connection[current][i]+dfs(i,totality+1,secquence+(1<<i),limit));
            }
            status[secquence][current]=ans;
            return ans;
        }
    }
    public static void main(String args[])throws IOException
    {
        StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        PrintWriter pw=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
        st.nextToken();
        int n=(int)st.nval;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            {
                st.nextToken();
                connection[i][j]=(int)st.nval;
            }
        pw.println(dfs(0,0,1,n));
        pw.flush();
    }
}

 

posted @ 2019-08-27 22:42  完全墨染的樱花  阅读(110)  评论(0编辑  收藏  举报