(内部题目不放题面了)
分析
- 之前考过一道原题但是数据过水 所以暴力能过……
- 先来说一下暴力的算法 首先对于1号节点 如果1号节点的入度为1 那么肯定无法回到1号节点 输出-1 最小环可以看成是从1号节点出发 然后经过一号节点的某一个可到达位置 再去一号节点的另一个可到达位置 然后加上这两个到1号节点的路径长 所以我们可以枚举一遍1号节点可到达的所有点 然后每次都跑一遍最短路 更新答案
- 但是暴力显然最坏的情况会退化成O((n^2)(log_n)) 显然是过不去这个题的 我们可以考虑分组 然后跑最短路
- 如果我们把1可以到达的一些点分为一组 另i一些点分为另外一组 然后建立超级源点向第一组的所有点都加边 权值为1到达该点的路径长 建立超级汇点向另外一组的所有点都加边 权值也为1到达该点的路径长 显然答案就是超级源点到超级汇点的最短路
- 接下来就是我们如何不漏情况的分组 对于两个点 我们必须令其至少一次在不同的两组中 这样才可能计算它对于答案的贡献 所以可以使用二进制分组 因为对于两个节点 编号肯定不同 编号不同则编号化成二进制后 也至少有一位是不同的 所以我们可以枚举每个二进制位 然后将该位是1的分成一组 该位是0的分成一组 这样可以保证不会漏掉答案
- 实现细节挺多的
Code
#include<bits/stdc++.h>
using namespace std;
#define rint register int
const int maxn = 2e4 + 10;
int head[maxn << 3];
int Head[maxn << 3];
int cnt;
int fz[] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384};
int ans;
int dis[maxn];
int vis[maxn];
struct node{
int next,to,dis;
}a[maxn << 3];
void add(int x,int y,int z){
a[++cnt].next = head[x];
a[cnt].to = y;
a[cnt].dis = z;
head[x] = cnt;
}
void init(){
cnt = 0;
ans = INT_MAX;
memset(head,0,sizeof(head));
}
deque<int> q;
void spfa(int s){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s] = 0; vis[1] = 1;
q.push_front(s);
while(!q.empty()){
rint u = q.front(); q.pop_front();
vis[u] = 0;
for(rint i = head[u];i;i = a[i].next){
rint v = a[i].to;
if(dis[v] > dis[u] + a[i].dis){
dis[v] = dis[u] + a[i].dis;
if(!vis[v]) {
if(q.empty()) q.push_front(v);
else if(dis[v] < dis[q.front()]) q.push_front(v);
else q.push_back(v);
}
vis[v] = 1;
}
}
}
}
int main(){
freopen("leave.in","r",stdin);
freopen("leave.out","w",stdout);
int T;scanf("%d",&T);
while(T--){
init();
int n,m;scanf("%d%d",&n,&m);
for(rint i = 1;i <= m;++i){
int u,v,z;scanf("%d%d%d",&u,&v,&z);
add(u,v,z);add(v,u,z);
}
for(rint i = head[1];i;i = a[i].next) Head[a[i].to] = head[a[i].to];
rint now = cnt;
for(rint i = 0;n >> i;++i){
cnt = now;
for(rint j = head[1];j;j = a[j].next){
rint v = a[j].to;
if(v & (1 << i)) add(n + 1,v,a[j].dis);
else add(v,n + 2,a[j].dis);
}
spfa(n+1);
ans = min(ans,dis[n+2]);
for(rint j = head[1];j;j = a[j].next) head[a[j].to] = Head[a[j].to];
head[n + 1] = head[n + 2] = 0;
}
if(ans != 0x3f3f3f3f) cout << ans << endl;
else cout << "-1" << endl;
}
return 0;
}