2020牛客国庆集训派对day2 B【Cheap Delivers】(最短路+状压dp)

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

题意:给你n个点,m条边(n,m<=1e4),k个任务(k<=18)。每次你只能执行一个任务(任务即从u点走到v点),问以最优走法完成所有任务的最短距离是多少。

题解:首先对于每个单独的任务而言,其u到v必定是走他们的最短路。所以ans1为所有任务(u->v)的最短路之和,这是不可避免的。接下来就要考虑优化合理分配任务的执行顺序了。当前的终点走到下一个点的起点取决于我们的选取方案。因为k的范围(k<=18)比较小,所以考虑用状态压缩dp枚举所有方案,优化到最新最优。

其中 dist[i][j] 表示从i的终点走到j点的起点所花费的距离,那么你对于每次任务都可以直接通过dijstra算法求出。

其中 dp[i][j] 表示当前状态是i,然后你目前位于j点的花费情况。先初始化dp为inf。那么显而易见的就是对于第i个任务而言,dp[1<<i][i] = dist[i][i]。

那么接下来就是状态转移了:

 

 最后就是对于结果的输出了:

 

 

AC代码:

#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define white 0
#define black 1
#define grey  2
#define ll long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
#define eps 0.000000001
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define IO ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f3f3f;
const int mod=1e9+7;
const int maxn=1e4+5;
int tot,head[maxn];
struct E{
    int to,next,w;
}edge[maxn<<1];
void add(int u,int v,int w){
    edge[tot].to=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot++;
}
int n,m,k;
ll d[maxn];ll color[maxn];
priority_queue<pair<ll,ll> >q; 
void Dijkstra(ll s){
    for(ll i=0;i<=n;i++) d[i]=inf,color[i]=white;
    d[s]=0;
    q.push(make_pair(0,s));
    color[s]=grey;
    while(!q.empty()){
        pair<ll,ll> f=q.top();
        q.pop();
        ll u=f.second;
        color[u]=black;
        if(d[u]<f.first*(-1)) continue;
        for(ll i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(color[v]==black) continue;
            if(d[v]>d[u]+edge[i].w){
                d[v]=d[u]+edge[i].w;
                q.push(make_pair(d[v]*(-1),v));
                color[v]=grey;
            }        
        }
    }
}

ll dist[20][20],dp[1<<18][18];
int st[20],ed[20];
int main(){
    scanf("%d%d%d",&n,&m,&k);mem(head,-1);
    for(int i=0;i<m;i++){
        int u,v,w;scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);add(v,u,w);
    }
    for(int i=0;i<k;i++){
        scanf("%d%d",&st[i],&ed[i]);
    }
    for(int i=0;i<k;i++){
        Dijkstra(ed[i]);
        for(int j=0;j<k;j++){
            dist[i][j]=d[st[j]];
        }
    }
    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]=dist[i][i];
    }
    for(int i=0;i<(1<<k);i++){
        for(int j=0;j<k;j++){
            if(i&(1<<j)){
                for(int p=0;p<k;p++){
                    if(i&(1<<p)) continue;
                    dp[i|(1<<p)][p]=min(dp[i|(1<<p)][p],dp[i][j]+dist[j][p]+dist[p][p]);
                }
            }
        }
    }
    ll ans=inf;
    for(int i=0;i<k;i++){
        ans=min(ans,dp[(1<<k)-1][i]);
    }
    if(ans>=inf) cout<<-1<<endl;
    else cout<<ans<<endl;
}
View Code

 

posted @ 2020-10-04 13:23  Anonytt  阅读(158)  评论(0编辑  收藏  举报