CHEAP DELIVERIES(状压dp)

题意

​ 给定有权无向图,给定 k ( k ≤ 18 ) k(k\le 18) k(k18)个任务,每个任务有起点和终点,求完成这 k k k个任务的最小路径和。


思路

​ 看到 k k k的范围显然是状压 d p dp dp,因为起点和终点确定,所以最短路也是确定的,可以先跑最短路预处理距离,然后进行状压 d p dp dp即可,令 d p [ s t ] [ i ] dp[st][i] dp[st][i]表示当前状态 s t st st下位于 e n d [ i ] end[i] end[i]这个位置的花费,对 s t st st没有的任务进行转移:

d p [ s t ∣ ( 1 < < j ) ] [ j ] = m i n ( d p [ s t ∣ ( 1 < < j ) ] [ j ] , d p [ s t ] [ i ] + d i s ( f r o m [ j ] , e n d [ j ] ) + d i s ( e n d [ i ] , f r o m [ j ] ) ) dp[st|(1<<j)][j]\\=min(dp[st|(1<<j)][j],dp[st][i]+dis(from[j],end[j])+dis(end[i],from[j])) dp[st(1<<j)][j]=min(dp[st(1<<j)][j],dp[st][i]+dis(from[j],end[j])+dis(end[i],from[j]))


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+5,M=2e4+5,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<long long,int>
#define fi first
#define se second
#define pb push_back
int n,m;
ll d[20][20],dp[1<<20][20];
ll dis[N],inf=1e17;
int vis[N],cnt,h[N];
struct edge{
    int to,nt,w;
}e[N<<1];
void add(int u,int v,int w){
    e[++cnt]={v,h[u],w},h[u]=cnt;
    e[++cnt]={u,h[v],w},h[v]=cnt;
}
void dij(int st){
    priority_queue<PII>q;
   for(int i=1;i<=n;i++) dis[i]=inf;
    mst(vis,0),dis[st]=0;
    q.push({0,st});
    while(!q.empty()){
        int u=q.top().se;
     //    printf("dis=%d\n",-q.front().fi);
        q.pop();
       
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=h[u];i;i=e[i].nt){
            int v=e[i].to,w=e[i].w;
            if(dis[v]>dis[u]+w&&!vis[v]){
                dis[v]=dis[u]+w;
                q.push({-dis[v],v});
            }
        }
    }
}
int k;
PII a[N];
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        int u,v,w;scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    for(int i=1;i<=k;i++) scanf("%lld%lld",&a[i].fi,&a[i].se);
    for(int i=1;i<=k;i++){
        dij(a[i].fi);
      //  printf("%d\n",dis[a[i].se]);
        for(int j=1;j<=k;j++) d[i][j]=dis[a[j].se]; 
    }
    for(int i=0;i<(1<<k);i++) for(int j=1;j<=k;j++) dp[i][j]=inf;
    for(int i=1;i<=k;i++) dp[1<<(i-1)][i]=d[i][i];
    for(int st=1;st<(1<<k);st++)
        for(int i=1;i<=k;i++){
            if((1<<i-1)&st){
                for(int j=1;j<=k;j++){
                    if(!((1<<j-1)&st)){
                        dp[st^(1<<j-1)][j]=min(dp[st^(1<<j-1)][j],
                                              dp[st][i]+d[j][i]+d[j][j]);
                       // printf("%d---\n",dp[st^(1<<j-1)][j]);
                    }
                }
            }
        }
    ll ans=inf;
    for(int i=1;i<=k;i++) ans=min(ans,dp[(1<<k)-1][i]);
    printf(ans==inf?"-1":"%lld\n",ans);
	return 0;
}

今天还做了类似的题,ABC的E,用 s p f a spfa spfa写的,差一点就 A K AK AK了,好可惜。
传送门

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=2e4+5,mod=1e9+7;
const ll inf=1e17;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int n,m;
vector<int>e[N];
int c[20];
int vis[N];
ll dp[1<<20][20],d[N];
ll dis[20][20];
void spfa(int st){
	queue<int>q;
	mst(vis,0);
	for(int i=1;i<=n;i++) d[i]=inf;
	d[st]=0,vis[st]=1;
	q.push(st);
	while(!q.empty()){
		int u=q.front();q.pop();vis[u]=0;
		for(int v:e[u]){
			if(d[v]>d[u]+1){
				d[v]=d[u]+1;
				if(!vis[v]) q.push(v),vis[v]=1;
			}
		}
	} 
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;scanf("%d%d",&u,&v);
		e[u].pb(v),e[v].pb(u);
	}int k;scanf("%d",&k);
	for(int i=0;i<k;i++) scanf("%d",&c[i]);
	for(int i=0;i<k;i++){
		spfa(c[i]);
		for(int j=0;j<k;j++) dis[i][j]=d[c[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]=1;
	for(int i=0;i<(1<<k);i++){
		for(int end=0;end<k;end++)
		 if((i>>end)&1){
 		for(int j=0;j<k;j++){
			if(!((i>>j)&1)){
				dp[i^(1<<j)][j]=min(dp[i^(1<<j)][j],dp[i][end]+dis[end][j]);
			}
		}
		}
	}
	int st=(1<<k)-1;
	ll ans=inf;
	for(int i=0;i<k;i++) if(dp[st][i]!=inf){
		ans=min(ans,dp[st][i]);
	}
	if(ans==inf) printf("-1\n");
	else printf("%lld\n",ans);
	return 0;
}