题解 P3106 【[USACO14OPEN]GPS的决斗Dueling GPS's】
2018.11.18 才发现venus大佬的评论,修改一个小错误 2018.10.2 修改部分解释,添加代码注释
貌似没有人用Dijkstra写呢。
其实这一题可以用Dij写的。思路其实很简单
我们可以写一个标准的dij模板,然后一共跑3遍。
分别是分3次重新构建图:
1.我们将GPS1的图存入邻接表。跑一遍dij
2.然后我们将GPS2的图再存入邻接表。再跑一遍dij
3.最后我们将2次跑过的dij,得到的最短路后所发出的警告数(分别不在2个Gps的次数)上当成边权。再跑一遍dij。
简单来说是3*汇点->源点某大佬指教的说法
每次要用不同的数组存。
最后输出dis3[1]最小警告数即可
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#define N 10005
#define M 50005
using namespace std;
void fastin(int &a){
char c=getchar();
a=0;
while((c<'0'||c>'9')&&c!='-'){ c=getchar();}
int f=1;
if(c=='-'){f=-1;c=getchar();}
while(c>='0'&&c<='9'){ a=(a<<1)+(a<<3)+c-48;c=getchar();}
a*=f;
}
int n,m;
int cnt=0;
struct Node
{
int from,to,VA1,VA2;
}E[M];
struct Rey
{
int nxt,to,VA;
}e[M];
int ds1[N];
int ds2[N];
int ds3[N];
struct pq
{
int to,VA;
bool operator < (const pq &x)const{
return VA>x.VA;//re define
}//dij需要使用堆,优先队列重定向。
};
int head[N];
int vis[N];
priority_queue<pq>q;
void ADDside(int u,int v,int w)
{
++cnt;
e[cnt].nxt=head[u];
e[cnt].VA=w;
e[cnt].to=v;
head[u]=cnt;
}
void Dijkstra(int u,int *ds)
{
pq now;
for(int i=1;i<=n;i++)
{
ds[i]=1<<30;
vis[i]=0;//初始化
}
now.to=u;
now.VA=ds[u]=0;
q.push(now);
while(!q.empty())
{
u=q.top().to;
q.pop();
if(vis[u])continue;//访问过了跳过
vis[u]=1;
for(register int i=head[u];i!=0;i=e[i].nxt)
{
int v=e[i].to;
int xl=ds[u]+e[i].VA;
if(vis[v]==0&&xl<ds[v])
{
ds[v]=xl;
now.to=v;
now.VA=ds[v];
q.push(now);//更新
}
}
}
}//标准dij
int main()
{
//freopen("gpsduel.in","r",stdin);
//freopen("gpsduel.out","w",stdout);
fastin(n);
fastin(m);
for(register int i=1;i<=m;i++)
{
fastin(E[i].to);
fastin(E[i].from);
fastin(E[i].VA1);
fastin(E[i].VA2);
}
memset(head,0,sizeof(head));
memset(e,0,sizeof(e));
cnt=0;//每次都要先初始化edge,head数组,cnt也要清理,因为要重存图
for(register int i=1;i<=m;i++)
{
ADDside(E[i].from,E[i].to,E[i].VA1);//存图
}
Dijkstra(n,ds1);
memset(head,0,sizeof(head));
memset(e,0,sizeof(e));
cnt=0;
for(register int i=1;i<=m;i++)
{
ADDside(E[i].from,E[i].to,E[i].VA2);//1次重构
}
Dijkstra(n,ds2);
memset(head,0,sizeof(head));
memset(e,0,sizeof(e));
cnt=0;
for(register int i=1;i<=m;i++)
{
int sum=0;
if(ds1[E[i].to]!=ds1[E[i].from]+E[i].VA1)
sum++;
if(ds2[E[i].to]!=ds2[E[i].from]+E[i].VA2)
sum++;
ADDside(E[i].from,E[i].to,sum); //2次重存
}
Dijkstra(n,ds3);
printf("%d",ds3[1]);//最少的警告数
putchar('\n');
return 0;
}