这一篇描述了关于最短路的简单变式
对于最后一道题,真正复杂度通过的做法是拆点。
类似于网络流的拆点,不过这里的拆点,是为了使得处理边权信息得到可能。
正常对点无法记录边权信息,我们可以考虑拆点将这样的信息暴露出来。
对于每个点发出的颜色,相当于可以独立出一个点。到达这个点只会可以通过这个点直接到达另一个点(不需要花费)。
有三个点
u
,
v
,
k
u,v,k
u,v,k,对于这三个点有两条边且颜色都是
c
c
c,如果处理只记录一次变化呢。
对于每个点发出的颜色都是一个新点
(
u
,
c
)
,
(
v
,
c
)
,
(
k
,
c
)
(u,c),(v,c),(k,c)
(u,c),(v,c),(k,c),这样子对于相邻边来说,一定是有公共节点的,如果相邻边颜色相同,公共节点发出的颜色相同的话,必然连接到一个节点,就能对到
k
k
k的节点实现更优。
最后的答案就是除以
2
2
2.
边权为0的颜色通道是便于瞬移来去除掉不必要的花费。
十分巧妙。值得细细品味。
#inlucde<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
#define ll long long
#define eps 1e-6
#define inf 0x3f3f3f3f3f3f3f3f
const int maxn=505000;
const int maxm=1500000;
map<pair<int,int>,int>M;
int n,m,cnt=0;
struct Edge{
int from,to;ll dist;
Edge(){}
Edge(int _from,int _to,ll _dist):from(_from),to(_to),dist(_dist){}
};
Edge ed[maxm];int he[maxn],ne[maxm],etop=1;
void insert(int u,int v,ll w){ed[etop]=Edge(u,v,w);ne[etop]=he[u];he[u]=etop++;}
ll d[maxn];
bool vis[maxn];
priority_queue<pair<ll, int>, vector<pair<ll, int> >, greater<pair<ll, int> > > q;
int dijkstra(int s,int t){
memset(vis,0,sizeof(vis));
memset(d, 0x3f, sizeof(d));
d[s] = 0;
q.push(make_pair(0, s));
while(!q.empty()){
int now = q.top().second;
q.pop();
if(!vis[now]){
vis[now] = true;
for(int i = he[now]; i; i = ne[i]){
Edge& e = ed[i];
if(d[e.to] > d[now] + e.dist){
d[e.to] = d[now] + e.dist;
q.push(make_pair(d[e.to], e.to));
}
}
}
}
return d[t]==inf?-2:d[t];
}
int get(int x,int z){
if(M[make_pair(x,z)])return M[make_pair(x,z)];
else M[make_pair(x,z)]=++cnt;
return cnt;
}
int main(){
cin>>n>>m;
cnt=n;
FOR(i,1,m){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
int tmpx=get(x,z),tmpy=get(y,z);
insert(x,tmpx,1);insert(tmpx,x,1);
insert(tmpx,tmpy,0);insert(tmpy,tmpx,0);
insert(tmpy,y,1);insert(y,tmpy,1);
}
printf("%d\n",dijkstra(1,n)/2);
}
接下来介绍一道题:按顺序访问一定量节点,最少走多少路。
注意边数只有
m
≤
n
+
30
m≤n+30
m≤n+30
最简单的做法的就是不断求两点间最短路。
更优秀的做法是:边数较少,对于一棵树我们可以用
l
c
a
lca
lca加前缀和
l
o
g
log
log级别的求出两点间最短距离,再判断每一条多余的边,对于一条多余的边,如果最短路不经过,那么不用考虑,经过了必须考虑。
判断是否经过比较困难,我们可以直接计算他们到两点之间的距离再加上边权,判断最小的即是答案。
对于这条边,从u->左边->右边->v,当然也有可能反过来。所以答案为:
m
i
n
(
d
i
s
左
[
u
]
+
d
i
s
右
[
v
]
+
w
,
d
i
s
左
[
v
]
+
d
i
s
右
[
u
]
+
w
,
s
u
m
[
u
]
+
s
u
m
[
v
]
−
2
s
u
m
[
l
c
a
]
)
min(dis左[u]+dis右[v]+w,dis左[v]+dis右[u]+w,sum[u]+sum[v]-2sum[lca])
min(dis左[u]+dis右[v]+w,dis左[v]+dis右[u]+w,sum[u]+sum[v]−2sum[lca])
最后加起来即可。
需要用到最小生成树、最短路、
l
c
a
lca
lca。
同时我们必须跑完所有多于边的最短路,记录每个点到其他点的最短路的时候需要离散化一下,不然不是
r
e
re
re就是
m
l
e
mle
mle。
这是一道简化图的过程,进阶的题目都不再是简单修改算法,而是修改图形去适应算法。
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
using namespace std;
int n,m,k;
const int maxn = 100500;
const int maxm = 201000;
struct Edge{
int from,to;ll dist;
Edge(){}
Edge(int _from,int _to,ll _dist):from(_from),to(_to),dist(_dist){}
friend bool operator < (Edge a,Edge b){return a.dist<b.dist;}};
Edge ed[maxm],edges[maxm];int he[maxn],ne[maxm],etop=1;
void insert(int u,int v,ll w){ed[etop]=Edge(u,v,w);ne[etop]=he[u];he[u]=etop++;}
bool flag[maxm];int change[maxn];
vector<pair<ll,int> >G[maxn];//新的树。
vector<pair<int,int> >last;//剩余的边
vector<ll>value;
int List[maxn];
map<pair<int,int>,int>judge;
int fx[maxn],ans=0;
int find(int x){return fx[x]==x?x:fx[x]=find(fx[x]);}
void kruskal(){
for(int i=1;i<=n;i++)fx[i]=i;
for(int i=1;i<etop;i++)edges[i]=ed[i];
sort(edges+1,edges+etop);
for(int i=1;i<etop;i++){
Edge &e = edges[i];
if(find(e.from)==find(e.to))continue;
else{
G[e.from].push_back(make_pair(e.dist,e.to));
G[e.to].push_back(make_pair(e.dist,e.from));
judge[make_pair(min(e.from,e.to),max(e.from,e.to))]=1;
fx[find(e.from)]=find(e.to);
}
}
}
ll d[205][maxn];
bool vis[maxn];
priority_queue<pair<ll, int>, vector<pair<ll, int> >, greater<pair<ll, int> > > q;
void dijkstra(int s){
memset(vis,0,sizeof(vis));
int use=change[s];
//cout<<s<<" "<<use<<endl;
memset(d[use],0x3f,sizeof(d[use]));
d[use][s]=0;
q.push(make_pair(0,s));
while(!q.empty()){
int now = q.top().second;
q.pop();
if(!vis[now]){
vis[now] = true;
for(int i = he[now]; i; i = ne[i]){
Edge& e = ed[i];
if(d[use][e.to] > d[use][now] + e.dist){
d[use][e.to] = d[use][now] + e.dist;
q.push(make_pair(d[use][e.to], e.to));
}
}
}
}
}
void init(){
kruskal();
for(int i=1;i<etop;i++)if(!judge[make_pair(min(ed[i].from,ed[i].to),max(ed[i].from,ed[i].to))]){
last.push_back(make_pair(ed[i].from,ed[i].to));
//cout<<ed[i].from<<" "<<ed[i].to<<endl;
judge[make_pair(min(ed[i].from,ed[i].to),max(ed[i].from,ed[i].to))]=1;
value.push_back(ed[i].dist);
}
for(int i=0;i<last.size();i++){
change[last[i].first]=(i+1);
change[last[i].second]=(i+1)+last.size();
dijkstra(last[i].first);
dijkstra(last[i].second);
}
etop=1;
memset(he,0,sizeof(he));
memset(ne,0,sizeof(ne));
for(int i=1;i<=n;i++)
for(int j=0;j<G[i].size();j++){
insert(i,G[i][j].second,G[i][j].first);
//cout<<i<<" "<<G[i][j].second<<endl;
}
}
#define K 21
int dep[maxn],root,f[22][maxn];
ll sum[maxn];
void dfs(int u,int fa){
for(int i=he[u];i;i=ne[i]){
Edge &e=ed[i];
if(e.to==fa)continue;
dep[e.to]=dep[e.from]+1;
f[0][e.to]=e.from;
sum[e.to]=sum[e.from]+e.dist;
dfs(e.to,e.from);
}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int d=dep[x]-dep[y];
for(int p=0,k=1;p<K;p++,k<<=1){
if(d&k)x=f[p][x];
}
if(x==y)return x;
for(int i=K-1;i>=0;i--){
if(f[i][x]==f[i][y])continue;
x=f[i][x],y=f[i][y];
}
return f[0][x];
}
void pre_lca(){
memset(dep,0,sizeof(dep));
int root=1;
dep[root]=1;
dfs(root,-1);
for(int j=1;j<K;j++)
for(int i=1;i<=n;i++)f[j][i]=f[j-1][f[j-1][i]];
}
int main(){
cin>>n>>m;
FOR(i,1,m){
int u,v;ll w;
scanf("%d%d%lld",&u,&v,&w);
insert(u,v,w);
insert(v,u,w);
}
init();
pre_lca();
ll ans=0;
cin>>k;List[1]=1;
FOR(i,1,k)scanf("%d",&List[i+1]);
for(int i=1;i<=k;i++){
int u=List[i],v=List[i+1];
int father=lca(u,v);
ll tmp=sum[u]+sum[v]-2*sum[father];
//cout<<u<<" "<<v<<" fa:"<<father<<" "<<tmp<<endl;
for(int j=0;j<last.size();j++){
int l=change[last[j].first],r=change[last[j].second];
//cout<<last[j].first<<" "<<last[j].second<<endl;
//cout<<d[l][u]<<" "<<d[r][v]<<")("<<d[l][v]<<" "<<d[r][u]<<endl;
ll tmptmp=min(d[l][u]+d[r][v]+value[j],d[l][v]+d[r][u]+value[j]);
tmp=min(tmp,tmptmp);
}
ans+=tmp;
}
cout<<ans<<endl;
}
/*
6 8
1 5 10
5 6 9
5 3 3
6 2 11
2 4 9
4 5 3
3 4 7
1 2 4
1
3
*/
现在我们再来介绍一道题,农夫带狼羊菜过河大家都听说过把。这个题目就是类似的,某个特殊的人可以带别人到对岸,也可以带回来,消耗的体力就是带的人数(包括自己)。同时有
m
m
m对情侣,必须保证情侣在同一边的时候,这个人一定也在这一边监视。求最少消耗体力使得所有人都到对岸。同时带人的时候,有最大限制
k
k
k
n
、
m
≤
15
n、m≤15
n、m≤15
显然易见,状压最短路。
初始状态为
(
1
<
<
n
+
1
)
−
1
(1<<n+1)-1
(1<<n+1)−1,最终状态为
0
0
0,最后一位表示的是特殊的人。
只需要事先暴力枚举剔除不合法状态即可。(遍历所有情侣判断是否对应位为1,特殊的人为0。或者反过来)
对于合法状态,我们需要建立有向边,就是判断合法转移。
考虑对
A
A
A和
B
B
B异或,如果最后一位异或为1,说明特殊的人进行了移动,就是合法条件之一。
异或对应位为1,表示发生了移动,总共移动人数必须小于等于
k
k
k。
并且带到另一边实际上,所以变化的一定都是同样的改变:都是由
0
−
>
1
,
1
−
>
0
0->1,1->0
0−>1,1−>0
所以我们可以简单地把这样的改变判断等价于
m
a
x
(
A
,
B
)
−
m
i
n
(
A
,
B
)
=
A
⊕
B
max(A,B)-min(A,B)=A\oplus B
max(A,B)−min(A,B)=A⊕B
如果都能够满足建双向边,跑最短路即可。
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
#define ll long long
#define eps 1e-6
#define inf 0x3f3f3f3f3f3f3f3f
const int maxn=200050;
const int maxm=400050;
int n,m,k;
pair<int,int>A[50];
vector<int>G;
struct Edge{
int from,to;ll dist;
Edge(){}
Edge(int _from,int _to,ll _dist):from(_from),to(_to),dist(_dist){}
};
Edge ed[maxm];int he[maxn],ne[maxm],etop=1;
void insert(int u,int v,ll w){ed[etop]=Edge(u,v,w);ne[etop]=he[u];he[u]=etop++;}
ll d[maxn];
bool vis[maxn];
priority_queue<pair<ll, int>, vector<pair<ll, int> >, greater<pair<ll, int> > > q;
void dijkstra(int s){
memset(vis,0,sizeof(vis));
memset(d, 0x3f, sizeof(d));
d[s] = 0;
q.push(make_pair(0, s));
while(!q.empty()){
int now = q.top().second;
q.pop();
if(!vis[now]){
vis[now] = true;
for(int i = he[now]; i; i = ne[i]){
Edge& e = ed[i];
if(d[e.to] > d[now] + e.dist){
d[e.to] = d[now] + e.dist;
q.push(make_pair(d[e.to], e.to));
}
}
}
}
}
bool check_1(int u,int x,int y){
if((u&1)==0){
//cout<<(u&(1<<x))<<" "<<(u&(1<<y))<<endl;
if((u&(1<<x))&&(u&(1<<y)))
return false;
}
if((u&1)==1){
if(((u&(1<<x))==0)&&((u&(1<<y))==0))
return false;
}
return true;
}
bool check_2(int x,int y,int& cnt){
int tmp=x^y;
if(tmp&1==1){
for(int i=0;i<n;i++){
int x=(1<<i);
if((tmp&x)>0)cnt++;
}
if(cnt<=k){
if(x<y)swap(x,y);
if(x-y==tmp)return true;
}
}
return false;
}
int main(){
cin>>n>>m>>k;n++;
for(int i=1;i<=m;i++){
int x,y;scanf("%d%d",&x,&y);
A[i]=make_pair(x,y);
}
for(int i=0;i<(1<<n);i++){
int ok = 1;
for(int j=1;j<=m;j++){
int x=A[j].first,y=A[j].second;
if(!check_1(i,x,y)){
ok=0;
break;
}
}
if(ok)G.push_back(i);//,cout<<i<<endl;
}
for(int i=0;i<G.size();i++)
for(int j=0;j<G.size();j++){
int cnt=0;
if(check_2(G[i],G[j],cnt)){
//cout<<G[i]<<" "<<G[j]<<":"<<cnt<<endl;
insert(G[i],G[j],cnt);
}
}
dijkstra(((1<<n)-1));
if(d[0]==inf)puts("mole");
else cout<<d[0]<<endl;
}