[状压dp] poweroj 2911: 送外卖的小鸽鸽Mannix

题目

题目链接:https://www.oj.swust.edu.cn/problem/show/2911

一个人要从0开始给k个地方送外卖最后回到起始点 求怎么送路程最短

思路

先处理图,把每个点之间的距离都用dij跑出来存下。
最后用状压dp, d p [ i ] [ j ] dp[i][j] dp[i][j]表示用状态 i i i到达 j j j的最小路径

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<ctime>
#include<iostream>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<vector>
#include<iomanip>
#include<list>
#include<bitset>
#include<sstream>
#include<fstream>
#include<complex>
#include<algorithm>
#if __cplusplus >= 201103L
#include <unordered_map>
#include <unordered_set>
#endif
#define int long long
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N =1e6+10;
struct sut{
    int to,next,val;
}edge[N*2];
int head[N],tot=0;
int dis[N],d[20][20];
bool vis[N];
void add_edge(int u,int v,int w){
    edge[++tot].to=v;
    edge[tot].val=w;
    edge[tot].next=head[u];
    head[u]=tot;
}   
int n,m,k;
void dij(int st){
    for(int i=0;i<=n;i++) dis[i]=INF,vis[i]=0;
    priority_queue<pair<int,int>> q;
    dis[st]=0;
    q.push({-dis[st],st});
    while(!q.empty()){
        auto now=q.top();
        q.pop();
        int u=now.second;
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=edge[i].next){
            int v=edge[i].to;
            int w=edge[i].val;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!vis[v]) q.push({-dis[v],v});
            }
        }
    }
      
}
int a[N];
int dp[(1<<12)][12];
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
 
    cin>>n>>m>>k;
    for(int i=1;i<=k;i++){
        cin>>a[i];
        if(a[i]==1) i--,k--;
    } 
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        add_edge(u,v,w);
        add_edge(v,u,w);
    }
    dij(1);
    for(int i=1;i<=k;i++){
        d[0][i]=dis[a[i]];
        d[i][0]=dis[a[i]];
    }
    for(int i=1;i<=k;i++){
        dij(a[i]);
        for(int j=1;j<=k;j++){
            d[i][j]=dis[a[j]];
            if(i==j) d[i][j]=0;
        }
    }
     
    memset(dp,INF,sizeof(dp));
    for(int i=0;i<(1<<k);i++){
        if(i==0){
            for(int j = 1;j<=k;j++) dp[(1<<(j-1))][j] = d[0][j];
        }else{
            for(int u = 1;u<=k;u++){
                if(i&(1<<(u-1))){
                    for(int v = 1;v<=k;v++){
                        dp[i|(1<<(v-1))][v] = min(dp[i|(1<<(v-1))][v],dp[i][u]+d[u][v]);
                    }
                }
            }
        }
    }
    int ans = INF;
    for(int i=1;i<=k;i++){
        ans=min(ans,dp[(1<<(k))-1][i]+d[i][0]);
    }
    cout<<ans<<endl;
         
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值