郊区春游 (状压dp)

链接:https://ac.nowcoder.com/acm/problem/16122

来源:牛客网

今天春天铁子的班上组织了一场春游,在铁子的城市里有n个郊区和m条无向道路,第i条道路连接郊区Ai和Bi,路费是Ci。经过铁子和顺溜的提议,他们决定去其中的R个郊区玩耍(不考虑玩耍的顺序),但是由于他们的班费紧张,所以需要找到一条旅游路线使得他们的花费最少,假设他们制定的旅游路线为V1, V,V... VR,那么他们的总花费为从V1到V2的花费加上V2到V3的花费依次类推,注意从铁子班上到V1的花费和从VR到铁子班上的花费是不需要考虑的,因为这两段花费由学校报销而且我们也不打算告诉你铁子学校的位置。

输入:

第一行三个整数n, m, R(2 ≤ n ≤ 200, 1 ≤ m ≤ 5000, 2 ≤ R ≤ min(n, 15))。
第二行R个整数表示需要去玩耍的郊区编号。
以下m行每行Ai, Bi, Ci(1 ≤ Ai, B≤ n, A≠ Bi, C≤ 10000)
保证不存在重边。

思路:TSP问题:

 第一步:确定状态
 F[st][i]表示当前状态为st,最后到达的一个点是i,所经过的最短距离
 第二步:确定状态转移方程
 F[st][i] = minn(f[st’][j] + a[j][i])
 其中 st’ + (1<<(j – 1)) ==s
st状态第i位为1表示,在必须去的郊区中第i个已经走过。

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<sstream>
#include<vector>
#include<stack>
#include<deque>
#include<cmath>
#include<map>
#include<queue>
#include<bitset>
//#include<hash_map>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
//using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int maxn=200+79;
const int mod=998244353;
const ll INF=0x3f3f3f3f;
const double pi=acos(-1);
int pos[30],cost[maxn][maxn],dp[50050][20];
void floyd(int n)
{
    fu(k,1,n)
        fu(i,1,n)
            fu(j,1,n)
            cost[i][j]=min(cost[i][j],cost[i][k]+cost[k][j]);
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    ms(cost,INF);ms(dp,INF);
    int n,m,r;cin>>n>>m>>r;
    fu(i,1,r) cin>>pos[i];
    fu(i,1,m)
    {
        int u,v,c;cin>>u>>v>>c;
        cost[u][v]=cost[v][u]=c;
    }
    floyd(n);
    int up=(1<<r)-1;
    fu(i,1,r) dp[(1<<(i-1))][i]=0;//初始化,各开始位置都未访问过
    fu(i,1,up)//枚举状态
    {
        fu(j,1,r)//上一个以j结尾的状态
        {
            int lp=1<<(j-1);
            if((i&lp)==0) continue;//以j结尾所有右数第j位要有1
            fu(k,1,r)//以k为结尾可放状态
            {
                int p=1<<(k-1);
                if(i&p) continue;//以k结尾新放上去的,所以右数第k位要没有旅游过
                dp[i|p][k]=min(dp[i|p][k],dp[i][j]+cost[pos[j]][pos[k]]);
            }
        }
    }
    int ans=INF;
    fu(i,1,r)
    {
        //r个位置要全部访问过,所以最终状态时11111即up
        ans=min(ans,dp[up][i]);
    }
    cout<<ans<<endl;
    return 0;
}

 

posted on 2020-08-18 11:11  Aminers  阅读(244)  评论(0编辑  收藏  举报

导航