暑假集训每日一题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;

}

 

你可能感兴趣的:(压缩)