2020牛客国庆集训派对day2 B.CHEAP DELIVERIES(状态压缩dp + dijkstra)

题目链接:https://ac.nowcoder.com/acm/contest/7818/B

示例

输入

5 5 3 
1 2 1 
2 3 2 
3 4 3 
4 5 4 
5 2 4 
2 3 
1 2 
5 3 

输出

12

分析

题目可以简化为有 k 条路,每条都要按规定走过。
我们可以想到,每条路要走到别的路肯定是从该路的结束点到其他路的开始点, k 最大18,那么我们可以给每条路按照这样的规则连边,然后用状态压缩来得到答案。

代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e4 + 7;
const ll inf = 0x3f3f3f3f;
const ll INF = 1e18;
int n,m,k;
vector<pair<int,int> > mp[N];
ll dis[N];
bool vis[N];
priority_queue<pair<ll,ll> > Q;
void dij(ll &s)
{
    int now,nxt;
 
    for(int i=1;i<=n;++i){
        dis[i]=INF;
        vis[i]=false;
    }
    while(!Q.empty())   Q.pop();
    Q.push(make_pair(0,s)),dis[s]=0;
    while(!Q.empty()){
        now=Q.top().second;
        Q.pop();
        if(vis[now]==true)  continue;
        vis[now]=true;
        for(int i=0;i<mp[now].size();++i){
            nxt=mp[now][i].first;
            if(!vis[nxt]&&dis[nxt]>dis[now]+mp[now][i].second){
                dis[nxt]=dis[now]+mp[now][i].second;
                Q.push(make_pair(-dis[nxt],nxt));
            }
        }
    }
}
 
const int P = 270000;
ll d[20][20],dp[P][18];
pair<ll,ll> pi[N];
 
int main()
{
    int f,t,l;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF){
        for(int i=1;i<=n;++i)
            mp[i].clear();
        for(int i=0;i<m;++i){
            scanf("%d%d%d",&f,&t,&l);
            mp[f].push_back(make_pair(t,l));
            mp[t].push_back(make_pair(f,l));
        }
        for(int i=1;i<=k;++i)
            scanf("%d%d",&pi[i].first,&pi[i].second);
        for(int i = 0;i < 20;++i)
            for(int j = 0;j < 20;++j)
                d[i][j] = INF;
        for(int i = 1;i <= k;++i){
            dij(pi[i].first);
            for(int j = 1;j <= k;++j)
                d[i][j]=dis[pi[j].second];
        }
        for(int i=0;i<(1<<k);++i)
            for(int j=0;j<=k;++j)
                dp[i][j]=INF;
        for(int i = 0;i < k;++i) dp[1 << i][i] = d[i + 1][i + 1];
        ll ans = inf;
        for(int i = 1;i < (1 << k);++i){
            for(int j = 0;j < k;++j){
                if(!(i & (1 << j))) continue;
                for(int l = 0;l < k;++l){
                    if(i & (1 << l)) continue;
                    dp[i|(1 << l)][l]=min(dp[i|(1 << l)][l],dp[i][j] + d[j + 1][l + 1] + d[l + 1][l  + 1]);
                }
                if(i + 1 == (1 << k)) ans = min(ans,dp[i][j]);
            }
        }
        printf("%lld\n",ans == inf ? -1 : ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值