正题
题目链接:https://ac.nowcoder.com/acm/contest/1104/B
题目大意
nnn个点mmm条边的无向图,kkk条必须经过的边,求从1出发经过这kkk条边再回到1的最短路。
解题思路
我们每条边两段的端点和1是我们需要用到的特征点,我们用计算出每个特征点的之间的最短距离,然后开始dpdpdp。
gi,jg_{i,j}gi,j表示经过的必经边的状态为iii,在第jjj个特征点的最短路,然后枚举转移即可。
时间复杂度O(knlogn+2k∗k2)O(kn\log n+2^k*k^2)O(knlogn+2k∗k2)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const ll N=51000;
struct edge_node{ll to,next,w;
}a[N*8];
struct node{ll num,w;
};
bool operator <(node x,node y)
{return x.w>y.w;}
priority_queue<node> q;
ll n,m,k,tot,ans=1e19,cnt,MS;
ll ls[N],f[26][N],g[4096][26],p[26];
bool v[N];
void addl(ll x,ll y,ll w)
{a[++tot].to=y;a[tot].next=ls[x];a[tot].w=w;ls[x]=tot;
}
void dij(ll num,ll s)
{memset(v,0,sizeof(v));q.push((node){s,0});f[num][s]=0;while(!q.empty()){ll x=q.top().num,w=q.top().w;q.pop();if(v[x]) continue;v[x]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(f[num][x]+a[i].w<f[num][y]){f[num][y]=f[num][x]+a[i].w;if(!v[y]) q.push((node){y,f[num][y]});}}}
}
int main()
{scanf("%lld%lld%lld",&n,&m,&k);p[++cnt]=1;for(ll i=1;i<=m;i++){ll x,y,w;scanf("%lld%lld%lld",&x,&y,&w);if(i<=k)p[++cnt]=x,p[++cnt]=y;addl(x,y,w);addl(y,x,w);}memset(f,127,sizeof(f));memset(g,127,sizeof(g));for(ll i=1;i<=cnt;i++)dij(i,p[i]);MS=1<<k;g[0][1]=0;for(ll i=0;i<MS;i++)for(ll j=1;j<=k;j++){if(i&(1<<j-1)) continue;ll z=i|(1<<j-1);for(ll ij=1;ij<=cnt;ij++)g[z][j*2]=min(g[z][j*2],g[i][ij]+f[ij][p[j*2+1]]+a[2*j].w),g[z][j*2+1]=min(g[z][j*2+1],g[i][ij]+f[ij][p[j*2]]+a[2*j].w);}for(ll i=1;i<=cnt;i++)ans=min(ans,g[MS-1][i]+f[i][1]);printf("%lld",ans);
}