暑假集训每日一题0720(状态压缩DP)

Description

 有N个点,有一个商人想经过所有的点恰好一次(商人最终不一定要回到起点),求商人需要走最短路程。

Input

 两个整数N,M表示图的点数和边数,接下去有M行,每行三个整数a ,b ,c 表示从a到b有一条无向边,长度是c

N<=15  c<=10000 

Output

 输出商人需要走过的最短距离 ,如果不能到达所有的点,输出-1 。

Sample Input

4 6
1 2 1
1 4 2
4 3 4
2 3 3
2 4 6
1 3 5

Sample Output

6
 
将已走过的点和未走过的点用二进制编码成整数,然后再DP.
例如:共4个点,1和2已走过,3和4未走,此时状态编码为二进制0011,对应整数为3。
状态设计为c[end][state]表示为当前状态为state,且最后访问的结点为end。
状态转移为:c[end][state]=MIN(c[i][s]+dist[i][end]),i为state的二进制中是1的位置,s为将state中的第end位变0后的整数。
边界条件为:state的二进制表示中只有1位为1,其余全为0,此时表示刚出发的状态,返回0。
需要注意的是在用位运算的时候一定要记得加括号,否则因为优先级的问题会出现一些不可预知的错误,例如RE。
View Code
#include <stdio.h>
#include <string.h>
#define MIN(a,b) ((a)<(b)?(a):(b))
#define N 15
#define INF 10000001
int n,m;
int d[N][N],c[N][1<<15];
int dp(int end,int state)
{
    int i,j,s,ans;
    if(c[end][state]!=-1)   return c[end][state];
    if((state&(state-1))==0)  return c[end][state]=0;
    s=state^(1<<end);
    ans=INF;
    for(i=0;i<n;i++)
    {
        if((s|(1<<i))==s)  ans=MIN(ans,dp(i,s)+d[i][end]);
    }
    return c[end][state]=ans;
}
int main()
{
    int i,j,ans;
    while(~scanf("%d%d",&n,&m))
    {
        for(i=0;i<n;i++)
        {
            for(j=0;j<i;j++)    d[i][j]=d[j][i]=INF;
            memset(c[i],-1,sizeof(int)*(1<<n));
        }
        while(m--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            a--,b--;
            d[a][b]=d[b][a]=MIN(d[a][b],c);
        }
        ans=INF;
        for(i=0;i<n;i++)    ans=MIN(ans,dp(i,(1<<n)-1));
        if(ans<INF) printf("%d\n",ans);
        else    puts("-1");
    }
    return 0;
}

 

posted @ 2012-07-20 16:40  BeatLJ  阅读(189)  评论(0编辑  收藏  举报