题目大意:
给你一个无向带权图,定义最短路为一条路径上各条边的权值异或,求从点1到点N的最短路径。
解题思路:
首先可以发现对于图上的路径异或,环非常特殊。因为无向图任意一条路径走过去再原路走回来不会造成任何花费,我们可以把环的花费异或到任意一条路径上而不需要额外的花费。而且我们可以通过向一条路径添加和路径有重复边的环来改变路径。
所以我们可以先随意建立一棵生成树,同时得到一条1到N的路径,非生成树上的边可以构成环,由于环非常多而且我们通过这个方式也没有得到的并不是环,只是所有环都可以有它们得到。所以需要利用高斯消元得到线性无关的向量组即可,然后保存下来。最终枚举把这些向量加到初始路径上的情况,得到的最小值即为答案。
感觉好像还是讲得不是很清楚-_-,附上官方题解:
AC代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <ctime>
using namespace std;
#define INF 0x3f3f3f3f
#define ULL unsigned long long
#define LL long long
#define fi first
#define se second
#define P pair<int,bool>
const int MAXV=100000+3;
int V, E;
vector<pair<int, int>> G[MAXV];//图的邻接表表示:to, cost
vector<int> circle;//保存环的花费高斯消元后的线性无关向量组
int dist[MAXV];//生成树上从根节点到当前结点花费
bool vis[MAXV];
void add(int x)//添加环,并且使每个环的二进制表示线性无关
{
for(int i=0;i<circle.size();++i)
x=min(x, x^circle[i]);
if(x)
circle.push_back(x);
}
void dfs(int u)//随意建立一棵生成树,然后找到环
{
vis[u]=true;
for(int i=0;i<G[u].size();++i)
{
int v=G[u][i].fi;
if(!vis[v])
{
dist[v]=dist[u]^G[u][i].se;
dfs(v);
}
else add(dist[v]^G[u][i].se^dist[u]);
}
}
int main()
{
scanf("%d%d", &V, &E);
for(int i=0;i<E;++i)
{
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
G[u].push_back(make_pair(v, c));
G[v].push_back(make_pair(u, c));
}
dfs(1);
for(int i=0;i<circle.size();++i)//高斯消元,使路径花费最小
dist[V]=min(dist[V], dist[V]^circle[i]);
printf("%d\n", dist[V]);
return 0;
}