CSU1129 送货到家 状压dp

来源:互联网 发布:windows 10镜像文件img 编辑:程序博客网 时间:2024/04/30 07:10

哈哈发现这道题竟然没有题解,于是我决定写一份!

状压dp


题目:

懒惰的巫女Reimu因为各种原因在香霖堂的店主Rinnosuke那儿欠下了很多债,于是乎只好靠帮他在幻想乡中送货来偿还掉微不足道的一小部分。

懒归懒,Reimu还是很聪明的,她希望送货的总路程尽可能的短,同时任何一个地方不经过两次。

简而言之,便是在一张图上找一个边权和最小经过所有点恰好一次的封闭回路。

Input

多组输入数据。

第一行为一个N(N<=15),即图上总共有多少个点。

之后共N行以邻接矩阵格式输入边的数据。

保证i->j与j->i的距离相同。

Output

每组数据仅一行,输出最小的封闭回路或者是“NoAnswer”。

Sample Input

50 1 3 3 11 0 1 3 33 1 0 1 33 3 1 0 11 3 3 1 0

Sample Output

5



是一道很典型的旅行商问题

不同之处是,大白上的旅行商问题是一定会有答案的,而且是从0这个点开始,这道题可能会有no answer的情况并且不是从0开始

当某个图从任意点出发都无法回到起点时,就输出noanswer

因为用邻接矩阵输入,所以预处理将0设成INF表示无法连通

dp数组不能太大 20的时候就爆内存了


初始化ans为INF 跑了一圈还是INF的话说明没走通就是没结果

刚开始把每个点作为起点都判了一遍 某一个无法连通的样例不知道为啥输出了结果

后来赵木杉学长启发 发现因为是无向图 所以并不需要判断以每个点作为起点 只用判断从0开始就可以了

因为要是能形成一个圈 以哪个为起点都是可以的 要是不能 以哪个为起点都是不行的


代码如下

#include <iostream>#include<stdio.h>#include<algorithm>#include<string>#include<cstring>#define INF 0x3f3f3f3fusing namespace std;int n;long long edge[20][20],dp[1<<16][16];bool vis[20];long long rec(int s,int v,int now){    if(dp[s][v]>=0)        return dp[s][v];    if(s==(1<<n)-1&&v==now)        return dp[s][v]=0;    long long res=INF;    for(int u=0;u<n;u++)    {        if(!(s>>u&1))        {            res=min(res,rec(s|1<<u,u,now)+edge[v][u]);        }    }    return dp[s][v]=res;}int main(){    while(scanf("%d",&n)!=EOF)    {        for(int i=0;i<n;i++)        {            for(int j=0;j<n;j++)            {                scanf("%lld",&edge[i][j]);                if(edge[i][j]==0)                edge[i][j]=INF;            }        }        memset(dp,-1,sizeof(dp));        int cnt=0;        for(int i=0;i<n;i++)        {            if(edge[0][i]==1)                cnt++;        }        long long ans=INF;        ans=min(ans,rec(0,0,0));        /*for(int i=0;i<n;i++)        {            ans=min(ans,rec(0,i,i));        }        */        if(ans!=INF)        printf("%lld\n",ans);        else        printf("NoAnswer\n");    }    return 0;}

提供一组noanswer的样例吧

4

0 1 0 0

1 0 2 4

0 2 0 3

0 4 3 0




0 0