题意:
链接:https://ac.nowcoder.com/acm/contest/1104/B
来源:牛客网
小多所在的城市可以看成是有n个点m条边的无向图(结点从1标号),每条边有一个距离,其中有k条边是小希特别想走过的k座大桥。
小多和小希现在呆在1号结点,请你帮小多规划一条最短路线,使得小多和小希能从当前位置出发,并经过这k座桥,最后回到结点1。
对于 100% 的数据,整张图联通,di≤1000000000
对于 100% 的数据,整张图联通,di≤1000000000
方法:在一个无向图中找一个经过节点1的最小环,使其必经k条边(k<=12)
看k的范围,易联想到状态压缩
12条边,24个点+节点1 一共25个必经点,其它点我们不必考虑,只需知道任意两点间的最短路dist
1.用25次dijkstra求出每个必经点到其它点的最短路
2.状压DP:dp[S][i]中S表示当前必经边的选择状态,i表示当前在节点i的最短路长度
枚举每一种状态,每一条边,每一个必经点,dp[S][u]=min(dp[last][j]+dist[j][v]+val(u,v))
初始化 dp[0][1]=0
目标 min(dp[S][i]+dist[i][1])
时间复杂度:O(nlogn+2^k)
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=200005;
const int mod=998244353;
const int inf=0x7fffffff;
priority_queue<pair<int,int> >q;
int n,m,k,ans=1e18; //long long最大值
int Next[maxn*2],to[maxn*2],head[maxn],val[maxn*2],tot=0;
int dist[26][maxn],cnt=0;
int dp[(1<<12)][26],e[maxn];
bool vis[maxn];
void add(int x,int y,int z){
to[++tot]=y; Next[tot]=head[x]; head[x]=tot; val[tot]=z;
}
int read(){
int x=0,f=1; char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') f=-1;
c=getchar();
}
while(c<='9'&&c>='0'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
void dijkstra(int x){
memset(vis,0,sizeof(vis));
memset(dist[x],0x3f,sizeof(dist[x]));
q.push(make_pair(0,e[x]));
dist[x][e[x]]=0;
while(!q.empty()){
int now=q.top().second; q.pop();
if(vis[now]) continue;
vis[now]=1;
for(int i=head[now];i;i=Next[i]){
int y=to[i];
if(dist[x][now]+val[i]<dist[x][y]){
dist[x][y]=dist[x][now]+val[i];
if(!vis[y]) q.push(make_pair(-dist[x][y],y));
}
}
}
}
signed main(){
// freopen("test.in","r",stdin);
n=read();m=read(); k=read();
e[++cnt]=1;
for(int i=1;i<=m;i++){
int x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
if(i<=k){
e[++cnt]=x;
e[++cnt]=y;
}
}
for(int i=1;i<=cnt;i++){
dijkstra(i);
}
//dp[i][j]表示状态为i(桥),当前处于第j个点
memset(dp,0x3f,sizeof(dp));
dp[0][1]=0;//第0状态 在节点1无需花费
for(int i=0;i<(1<<k);i++){
for(int j=1;j<=k;j++){ //当前处于第j条边,且第j条边未取
if(i&(1<<(j-1))) continue;
int now=(i|(1<<(j-1))); //将该位赋值1
for(int t=1;t<=cnt;t++){
dp[now][j*2]=min(dp[now][j*2],dp[i][t]+dist[t][e[j*2+1]]+val[j*2]);
dp[now][j*2+1]=min(dp[now][j*2+1],dp[i][t]+dist[t][e[j*2]]+val[j*2]);
}
}
}
for(int i=1;i<=cnt;i++) ans=min(ans,dp[(1<<k)-1][i]+dist[i][1]);
printf("%lld\n",ans);
return 0;
}
#define int long long
using namespace std;
const int maxn=200005;
const int mod=998244353;
const int inf=0x7fffffff;
priority_queue<pair<int,int> >q;
int n,m,k,ans=1e18; //long long最大值
int Next[maxn*2],to[maxn*2],head[maxn],val[maxn*2],tot=0;
int dist[26][maxn],cnt=0;
int dp[(1<<12)][26],e[maxn];
bool vis[maxn];
void add(int x,int y,int z){
to[++tot]=y; Next[tot]=head[x]; head[x]=tot; val[tot]=z;
}
int read(){
int x=0,f=1; char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') f=-1;
c=getchar();
}
while(c<='9'&&c>='0'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
void dijkstra(int x){
memset(vis,0,sizeof(vis));
memset(dist[x],0x3f,sizeof(dist[x]));
q.push(make_pair(0,e[x]));
dist[x][e[x]]=0;
while(!q.empty()){
int now=q.top().second; q.pop();
if(vis[now]) continue;
vis[now]=1;
for(int i=head[now];i;i=Next[i]){
int y=to[i];
if(dist[x][now]+val[i]<dist[x][y]){
dist[x][y]=dist[x][now]+val[i];
if(!vis[y]) q.push(make_pair(-dist[x][y],y));
}
}
}
}
signed main(){
// freopen("test.in","r",stdin);
n=read();m=read(); k=read();
e[++cnt]=1;
for(int i=1;i<=m;i++){
int x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
if(i<=k){
e[++cnt]=x;
e[++cnt]=y;
}
}
for(int i=1;i<=cnt;i++){
dijkstra(i);
}
//dp[i][j]表示状态为i(桥),当前处于第j个点
memset(dp,0x3f,sizeof(dp));
dp[0][1]=0;//第0状态 在节点1无需花费
for(int i=0;i<(1<<k);i++){
for(int j=1;j<=k;j++){ //当前处于第j条边,且第j条边未取
if(i&(1<<(j-1))) continue;
int now=(i|(1<<(j-1))); //将该位赋值1
for(int t=1;t<=cnt;t++){
dp[now][j*2]=min(dp[now][j*2],dp[i][t]+dist[t][e[j*2+1]]+val[j*2]);
dp[now][j*2+1]=min(dp[now][j*2+1],dp[i][t]+dist[t][e[j*2]]+val[j*2]);
}
}
}
for(int i=1;i<=cnt;i++) ans=min(ans,dp[(1<<k)-1][i]+dist[i][1]);
printf("%lld\n",ans);
return 0;
}