状态压缩dp入门--------TSP问题

来源:互联网 发布:西游记 女儿情 知乎 编辑:程序博客网 时间:2024/05/22 11:59


TSP问题
给你n个城市和城市之间的通路的长度,请你找出一条经过所有城市一次且仅经过一次的路线,使得这条路线的长度最短。


问题分析,如果要设计一个状态的话,显然状态与已经走过的城市和你当前所在的城市有关,现在,按照一定的顺序给每个城市一个编号,如果已经走过的城市记为1,没走过的城市记为0,那么已经走过的城市的状态就可以压缩成一个数。所以,该题目的状态表示为:

i=5~000101表示点1和点3已被访问
dp[i][j]表示分析在当前的状态i下,路径都为已经访问过的城市,找一条最短路径到达的j的总距离
       相应的状态转移方程为dp[ i ][ j]=min( dp[ i ^ (1<<j) ][ k ] + dis[ k ][ j ]  );    

 i ^ (1<<j)的意思是将j这个城市从i状态中去掉。 dis[ k][ j ] 是k和j之间的距离。


TSP基础问题

memset(dp, inf, sizeof(dp));  for (int i = 1; i <= n; i++)      dp[1<<(i - 1)][i] = 0;  int ans = inf;  for (int i = 0; i < (1<<n); i++) //状态的个数,从n个0到n个1     for (int j = 1; j <= n; j++)     {          if (i&(1<<(j - 1))) //必须是访问过的点j才可以,访问过的才知道最优子结构        {              for (int k = 1; k <= n; k++)                 if (i&(1<<(k - 1)) != 0 && g[k][j] != -1) //必须是访问过的k点且边存在            dp[i][j] = min(dp[i^(1<<(j - 1))][k] + g[k][j], dp[i][j]);          }          if (i == (1<<n) - 1)          ans = min(ans, dp[i][j]);      }  if (ans == inf)     return -1;  return ans;  

hdu 3001

   After coding so many days,Mr Acmer wants to have a good rest.So travelling is the best choice!He has decided to visit n cities(he insists on seeing all the cities!And he does not mind which city being his start station because superman can bring him to any city at first but only once.), and of course there are m roads here,following a fee as usual.But Mr Acmer gets bored so easily that he doesn't want to visit a city more than twice!And he is so mean that he wants to minimize the total fee!He is lazy you see.So he turns to you for help.
Input
There are several test cases,the first line is two intergers n(1<=n<=10) and m,which means he needs to visit n cities and there are m roads he can choose,then m lines follow,each line will include three intergers a,b and c(1<=a,b<=n),means there is a road between a and b and the cost is of course c.Input to the End Of File.
Output
Output the minimum fee that he should pay,or -1 if he can't find such a route.
Sample Input
2 11 2 1003 21 2 402 3 503 31 2 31 3 42 3 10
Sample Output
100907 
题意:n个点m条边,求某个点出发所有城市都要到达,每个城市最多可以被访问2次,求最短距离

题解:使用3进制存储状态:0,1,2次访问

#include<iostream>#include<cmath>#include<cstring>#include<cstdio>#include<vector>#include<algorithm>#define inf 0x3f3f3f3f#define ll long longusing namespace std;int w[20][20];int dp[60000][20];int bit[12] = {0,1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049};//0001,0010,0100,1000...对应的十进制数int num[60000][20];//某个十进制数的3进制形式存入numint n,m;void make_bit(){    for(int i=1;i<59050;i++)    {        int n=i,j=1;        while(n)        {            num[i][j++]=n%3;            n=n/3;        }    }}int main(){    make_bit();    while(cin>>n>>m)    {        memset(w,-1,sizeof(w));        for(int i=1;i<=m;i++)        {            int a,b,c;            cin>>a>>b>>c;            if(w[a][b]==-1)                w[a][b]=w[b][a]=c;            else                w[a][b]=w[b][a]=min(w[a][b],c);//防止出现重边        }        memset(dp,inf,sizeof(dp));        for(int i=1;i<=n;i++)            dp[bit[i]][i]=0;        int ans=inf;        for(int i=1;i<bit[n+1];i++)        {            bool flag=0;//i状态是否都不为0            for(int j=1;j<=n;j++)            {                if(num[i][j]==0)                {                    flag=1;                    continue;                }                for(int k=1;k<=n;k++)                    if(num[i][k]!=0&&w[k][j]!=-1)                        dp[i][j]=min(dp[i][j],dp[i-bit[j]][k]+w[k][j]);//i-bit[j]就是去除i状态中访问的1次j            }            if(!flag)                for(int j=1;j<=n;j++)                    ans=min(ans,dp[i][j]);        }        if(ans==inf)            ans=-1;        cout<<ans<<endl;    }    return 0;}




0 0