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; }
前ICPC算法竞赛退役选手|现摸鱼ing