思路:
考虑状态压缩动态规划。
定义 \(dp_{i,j,S}\) 表示点 \(j\) 离起点 \(i\) 的距离,且从点 \(j\) 开始打通的点集为 \(S\) 的最小代价(注意 \(S\) 不能包含 \(j\))。
考虑枚举 \(S\) 一个一个子集 \(S'\),同时枚举一个 \(k\),需要满足 \(k \in S'\),即我们可以先打通 \(j \to k\),然后由 \(k\) 来打通 \(S'\),由 \(j\) 来打通 \(S \setminus S'\),则状态转移方程为:
\[dp_{i,j,S} = \min\limits_{k \in S' \subseteq S} W(i,k) \times (i+1) + dp_{i+1,k,S' \setminus \{k\}} + dp_{i,j,S \setminus S'} \]时间复杂度为 \(O(3^nn^3)\)。
注意要先预处理出每个子集状态第一位为 \(1\) 的位置,方便枚举 \(k\),可以用 lowbit
实现。
完整代码:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
const ll N=12,M=(1ll<<N)+10,INF=1e9;
bool Begin;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
ll n,m,u,v,w,ans=INF;
ll W[N][N],id[M];
ll dp[N][N][M];
bool End;
int main(){
// open("A.in","A.out");
n=read(),m=read();
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
W[i][j]=INF;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
for(int k=1;k<(1ll<<n);k++)
dp[i][j][k]=INF;
while(m--){
u=read()-1,v=read()-1,w=read();
W[u][v]=W[v][u]=min({W[u][v],w});
}
for(int x=1,i=0;i<n;i++){
id[x]=i;
x<<=1ll;
}
for(int i=n-2;i>=0;i--){
for(int j=0;j<n;j++){
for(int S=1;S<(1ll<<n);S++){
if((S>>j)&1ll)
continue;
for(int T=S;T;T=(T-1)&S){
if(dp[i][j][S^T]>=dp[i][j][S])
continue;
for(int G=T;G;G^=lowbit(G)){
ll k=id[lowbit(G)];
if(W[j][k]==INF||j==k)
continue;
dp[i][j][S]=min(dp[i][j][S],W[j][k]*(i+1)+dp[i+1][k][T^(1ll<<k)]+dp[i][j][S^T]);
}
}
}
}
}
for(int i=0;i<n;i++)
ans=min(ans,dp[0][i][((1ll<<n)-1)^(1ll<<i)]);
write(ans);
return 0;
}
标签:NOIP2017,int,ll,long,P3959,pair,宝藏,dp,define
From: https://www.cnblogs.com/rgw2010/p/18342876