CODEVS 2800 送外卖

来源:互联网 发布:讨厌大野智 知乎 编辑:程序博客网 时间:2024/04/29 01:52

题目描述:从0号点出发,经过1-n点,不要求顺序,而且每个点可以走多次,最后回到0点,求最短时间。
因为要走遍1-n点,那么直接最短路是不行的,不保证没有环,所以dfs时间会多一些,而且这里dfs不好剪枝,那么就可以考虑一下dp,状态就是到某个点,走过了一些点,所用的最短时间,那么就要记录走过了哪些点,用bool数组存并不好,因为dp数组要开二维,而且判断哪些点走过时还要去枚举每个点去判断,既费时又费力。所以这个蹩脚的事情推动IT精英们去发明了状态压缩,留给我们使用。
状态压缩,顾名思义,就是压缩了状态(怎么感觉什么都没有说),直接拿这个题当栗子,有三个点,那么关于这三个点有没有走过的问题,可以用这些数来表示:000、001、010、011、100、101、110、111。很容易看出这是二进制,而且一共2ⁿ个,状态就这样地表示出来了,那么怎么这样怎么进行状态记录和转移呢,在我们新到达点i的时候我们可以让状态 +=2的i次方 ;我们查询点i有没有被走过,可以让状态 |2的i次方,这样处理完之后如果状态改变了那么就说明点i没有被走过(只有在i那个位置为0的时候才会改变)。
另外这个题要提前floyd求一下每两个点之间的最短路。

#include<cstdio>#include<iostream>#include<cstring>using namespace std;int g[20][20],dp[20][(1<<16)];int min(int a,int b){    return (a<b)?a:b;}int main(){    int n;    cin>>n;    for (int i=0;i<=n;i++)        for (int j=0;j<=n;j++)            scanf("%d",&g[i][j]);    for (int k=0;k<=n;k++)        for (int i=0;i<=n;i++)            if (i!=k)                for (int j=0;j<=n;j++)                    if (j!=k&&j!=i)                        g[i][j]=min(g[i][j],g[i][k]+g[k][j]);    memset(dp,127/3,sizeof(dp));    dp[0][0]=0;    int len=(1<<(n+1))-1;    for (int k=0;k<=len;k++)    //枚举走的情况        for (int i=0;i<=n;i++)  //枚举终点,下面那个是枚举起点        {            if ((k|(1<<i))!=k) continue;            for (int j=0;j<=n;j++) dp[i][k]=min(dp[i][k],min(dp[j][k-(1<<i)],dp[j][k])+g[j][i]);        }           cout<<dp[0][len];    return 0;}
0 0
原创粉丝点击