*点击

售货员的难题

题目

Description

某乡有n个村庄(1<n<15),有一个售货员,他要到各个村庄去售货,各村庄之间的路程s(0<s<1000)是已知的,且A村到B村与B村到A村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为1,他不知道选择什么样的路线才能使所走的路程最短。请你帮他选择一条最短的路。

Input

村庄数n和各村之间的路程(均是整数)。

Output

最短的路程

Sample Input

3        {村庄数}                        
0 2 1    {村庄1到各村的路程}
1 0 2    {村庄2到各村的路程}
2 1 0    {村庄3到各村的路程}

Sample Output

3

思路

传球游戏之最小总代价差不多的一道状压$dp$ 题;

有兴趣的读者可以看看传球游戏之最小总代价这道题;

那么这一题有什么不一样的呢,

首先售货员必须从$1$出发,然后最后还必须回到$1$;

我们只需在传球游戏之最小总代价这题的代码上;

将初始化改成$dp[1][1]=0$,最后统计$min(ans)$ 的时候加上 $i$到$1$ 的距离就ok了;

 

代码

#include<bits/stdc++.h>
#define re register
typedef long long ll;
using namespace std;
inline ll read()
{
    ll a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
ll n;
ll a[20][20];
ll dp[20][1<<15];
int main()
{
    memset(dp,127/3,sizeof(dp));//初始化 
    n=read();
    for(re ll i=1;i<=n;i++)
    for(re ll j=1;j<=n;j++)
        a[i][j]=read();//读入 
    dp[1][1]=0;//预处理 
    for(re ll k=0;k<=(1<<n)-1;k++)//枚举每个集合 
    for(re ll i=1;i<=n;i++)
    {
        if(!(k&(1<<(i-1))))//如果i 不在这个集合,那么进入下个循环 
            continue;
        for(re ll j=1;j<=n;j++)
        {
            if(i==j)//每个人不能从自己手里接过球 
                continue;
            if(k&(1<<(j-1)))//必须集合包涵 j ,也就是球传到 j,才能从 j 转移过来 
                dp[i][k]=min(dp[i][k],dp[j][k^(1<<(i-1))]+a[j][i]);//状态转移方程 
        }//修改代码艰难痕迹 
//        cout<<i<<" "<<k<<endl;
//        cout<<dp[i][k]<<endl;
    }
    ll ans=1<<30;
    for(re ll i=2;i<=n;i++)
        ans=min(ans,dp[i][(1<<n)-1]+a[i][1]);//find answer 
    printf("%lld\n",ans);//输出
    //return 0; 
}

 

posted @ 2020-08-10 22:53  木偶人-怪咖  阅读(170)  评论(0编辑  收藏  举报
*访客位置3D地图 *目录