建议做题顺序:
(其实数据量小一般floyd,但其他方法也是可以的)
dijk: 1 (7 ) 4 10 3 (2) 16
spfa:(10) 12 13 5 15 18
floyd: 6 7 9 2 14
差分约束:19 11
最后剩下一个第17题,涉及网络流,留在网络流专题做。
做题顺序来自传送门
Til the Cows Come Home (poj2387)
Sample Input
5 5
1 2 20
2 3 30
3 4 20
4 5 20
1 5 100
Sample Output
90
板子题不多赘述。
- 本题用到了邻接表存边(可以存的下)(链式前向星存图,+ dij )
这样不用考虑重边- 本题的输入是先边,后节点数
对于不懂链式前向星的,我推荐一篇blog(链式前向星是邻接表的一种)
AC
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#define fzhead EDGE(int _to, int _v, int _next)
#define fzbody to(_to), v(_v), next(_next)
#define For(i,x,y) for (register int i=(x);i<=(y);i++)
#define FOR(i,x,y) for (register int i=(x);i<=(y);i++)
#define mp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int>pa;
typedef pair<ll,ll>PA;
inline int read()
{
int ans=0;
char c=getchar();bool neg=false;
while(c<'0'||c>'9')
{
if(c=='-')neg=true;
c=getchar();
}
while(c>='0'&&c<='9')
{
ans=ans*10+c-'0';
c=getchar();
}
return (neg)?-ans:ans;
}
void write(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)write(x/10);
putchar(x%10+'0');
}
const int maxn=1e3+10;
int head[maxn<<1], dis[maxn], vis[maxn];
int cnt,n,m;
struct EDGE
{
int to,next,v;
EDGE(){}
fzhead:fzbody{}
}e[maxn<<1];
void add(int bg, int ed, int v)
{
e[++cnt]=EDGE(ed, v, head[bg]);
head[bg]=cnt;
}
inline void dij(int s)
{
For(i, 1, n)vis[i]=0;
priority_queue<pa>q;
dis[s]=0;q.push(mp(0,s));
while(!q.empty())
{
int u=q.top().se;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u]; i!=-1; i=e[i].next)
{
if(dis[e[i].to]>dis[u]+e[i].v)
{
dis[e[i].to]=dis[u]+e[i].v;
q.push(mp(-dis[e[i].to],e[i].to));
}
}
}
}
int main()
{
m=read(),n=read();
memset(dis,0x3f,sizeof(dis));
memset(head, -1, sizeof(head));
For(i,1,m)
{
int x=read(),y=read(),v=read();
add(x,y,v);
add(y,x,v);
}
dij(n);
write(dis[1]);
return 0;
}
MPI Maelstrom (poj1502)
(floyd板子题,加一个stl读数据)
Sample Input
5
50
30 5
100 20 50
10 x x 10
Sample Output
35
预备知识
atoi和stoi的区别
①atoi()的参数是 const char* ,因此对于一个字符串str我们必须调用 c_str()的方法把这个string转换成 const char类型的,而stoi()的参数是const string,不需要转化为 const char*;
②stoi()会做范围检查,默认范围是在int的范围内的,如果超出范围的话则会runtime error!而atoi()不会做范围检查,如果超出范围的话,超出上界,则输出上界,超出下界,则输出下界;
————————————————
版权声明:本文为CSDN博主「liuwxye」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_30366449/article/details/97921986
- atoi(s) 是STL的一个函数,可以把一个字符数组s转化成整型数字。
- atoi对应char,stoi 对应string
- 数据小,直接floyd
哎,读题读半天英语还是不过关。。
本题求如何可以从1到所有其他节点的路径最小。
先求一遍单源最短路之后。
之后求出最短路的最大值。
AC(floyd)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF=0x3f3f3f3f;
char s[110];
int cost[110][110];
int n;
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
cost[i][j]=(i==j)?0:INF;//init
}
for(int i=2; i<=n; i++)
{
for(int j=1; j<i; j++)
{
scanf("%s",s);
if(s[0]!='x')cost[i][j]=cost[j][i]=atoi(s);
}
}
for(int k=1; k<=n; k++)
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
cost[i][j]=min(cost[i][j],cost[i][k]+cost[k][j]);
}
}
}
int ans=0;
for(int i=1; i<=n; i++)
if(cost[1][i]!=INF)ans=max(cost[n][i],ans);
cout<<ans<<endl;
return 0;
}
AC(dij)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#define fzhead EDGE (int _to, int _v, int _next)
#define fzbody to(_to), v(_v), next(_next)
#define For(i,x,y) for (register int i=(x); i<=(y); i++)
#define mp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int>pa;
typedef pair<ll,ll>PA;
const int maxn=1e5+10;
inline int read()
{
int ans=0;bool neg=false;
char c=getchar();
while(c>'9'||c<'0')
{
neg=true;
c=getchar();
}
while(c>='0'&&c<='9')
{
ans=ans*10+c-'0';
c=getchar();
}
return (neg)?-ans:ans;
}
void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
struct EDGE
{
int to,next,v;
EDGE(){}
fzhead : fzbody{}
}e[maxn];
int n,m,cnt;
int vis[maxn],dis[maxn],head[maxn];
void add(int bg, int ed, int v)
{
e[++cnt]=EDGE(ed,v,head[bg]),head[bg]=cnt;
}
inline void dij(int s)
{
For(i,1,n)vis[i]=0;
priority_queue<pa>q;
dis[s]=0;q.push(mp(0,s));
while(!q.empty())
{
int u=q.top().se;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u]; i!=-1; i=e[i].next)
{
if(dis[e[i].to]>dis[u]+e[i].v)
{
dis[e[i].to]=dis[u]+e[i].v;
q.push(mp(-dis[e[i].to],e[i].to));
}
}
}
}
int main()
{
int n;
memset(dis,0x3f,sizeof(dis));
memset(head,-1,sizeof(head));
n=read();
char s[100];
for(int i=2; i<=n; i++)
{
for(int j=1; j<i; j++)
{
scanf("%s",s);
if(s[0]!='x')
{
int cost=atoi(s);
add(i,j,cost);
add(j,i,cost);
}
}
}
// cout<<"ok"<<endl;
dij(1);
int ans=0;
for(int i=1; i<=n; i++)
{
ans=max(dis[i],ans);
}
// cout<<"ok"<<endl;
write(ans);
putchar('\n');
return 0;
}
Silver Cow Party(poj3268)
Sample Input
4 8 2
1 2 4
1 3 2
1 4 7
2 1 1
2 3 5
3 1 2
3 4 4
4 2 3
Sample Output
10
题意:
本题描述了有n头牛要去参加一个party。
题目中每头牛分布在每个节点上,有一个节点x是聚会的举办地点。
n头牛要去参加聚会,牛门都很聪明,会走最短路。(有m条边即m条路)
题目中的路都是有向的。求在所有牛中,哪头牛来回的时间最长(路程)。
解法:(dij)
进行正向和反向建图。(把问题转化为单源最短路)求由x到其他节点的最短路。分别求两次。之后再for找最大值即可(dis1【i】+dis2【i】)。
总结教训:
函数里的a【】只是一个指针,不能直接sizeof(memset赋值的)
AC(dij)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define fzhead EDGE (int _to, int _v, int _next)
#define fzbody to(_to),v(_v),next(_next)
#define For(i,x,y) for (register int i=(x);i<=(y);i++)
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int>pa;
typedef pair<ll,ll>PA;
const int maxn=1e5+10;
inline int read()
{
int ans=0;
char c=getchar();bool neg=false;
while(c<'0'||c>'9')
{
if(c=='-')neg=true;
c=getchar();
}
while(c>='0'&&c<='9')
{
ans=ans*10+c-'0';
c=getchar();
}
return (neg)?-ans:ans;
}
void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
struct EDGE
{
int to,next,v;
EDGE(){}
fzhead : fzbody{}
}e1[maxn],e2[maxn];
int n,m,cnt1,k,cnt2;
int vis[maxn],dis1[maxn],head1[maxn],dis2[maxn],head2[maxn];
void add(int bg, int ed, int v,EDGE e[maxn],int &cnt,int head[maxn])
{
e[++cnt]=EDGE(ed,v,head[bg]);
head[bg]=cnt;
}
inline void dij(EDGE e[], int dis[],int head[])
{
memset(dis,0x3f,sizeof(dis1));
For(i,1,n)vis[i]=0;
priority_queue<pa>q;
dis[k]=0;
q.push(mp(0,k));
while(!q.empty())
{
int u=q.top().se;q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u]; i!=-1; i=e[i].next)
{
if(dis[e[i].to]>dis[u]+e[i].v)
{
dis[e[i].to]=dis[u]+e[i].v;
q.push(mp(-dis[e[i].to],e[i].to));
}
}
}
}
int main()
{
memset(head1, -1, sizeof(head1));
memset(head2,-1,sizeof(head2));
n=read();m=read();k=read();
for(int i=1; i<=m; i++)
{
int x,y,v;
x=read(),y=read(),v=read();
add(x,y,v,e1,cnt1,head1);
//cout<<e1[cnt1].to<<' '<<e1[cnt1].next<<' '<<e1[cnt1].v<<endl;
add(y,x,v,e2,cnt2,head2);
//cout<<e2[cnt2].to<<' '<<e2[cnt2].next<<' '<<e2[cnt2].v<<endl;
// cout<<endl;
}
dij(e1,dis1,head1);
dij(e2,dis2,head2);
// for(int i=1; i<=n; i++)cout<<dis1[i]<<endl;
int ans=0;
for(int i=1; i<=n; i++)
{
ans=max(dis1[i]+dis2[i],ans);
}
write(ans);
putchar('\n');
return 0;
}
Invitation Cards(poj1511)
Sample Input:
2
2 2
1 2 13
2 1 33
4 6
1 2 10
2 1 60
1 3 20
3 4 10
2 4 5
4 1 50
Sample Output
46
210
题意:
本题要你从1节点出发,把所有志愿者送到各个节点。
之后再把志愿者送回来。求最短路。
思路:
反向建边,分别求两次dij,之后把从1到每个点的dis,加起来即可。
总结教训
- 不开longlong见祖宗
- 初始化要彻底。
AC(dij)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define fzhead EDGE (long long _to, long long _v, long long _next)
#define fzbody to(_to), v(_v), next(_next)
#define For(i,x,y) for (register int i=(x);i<=(y);i++)
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int>pa;
typedef pair<ll,ll>PA;
inline ll read()
{
ll ans=0;
char ch=getchar();bool neg=false;
while(ch<'0'||ch>'9')
{
if(ch=='-')neg=true;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ans=ans*10+ch-'0';
ch=getchar();
}
return (neg)?-ans:ans;
}
void write(ll x)
{
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
const int maxn=1e6+10;
struct EDGE
{
ll to,next,v;
EDGE(){}
fzhead : fzbody{}
}e1[maxn],e2[maxn];
ll cnt1,cnt2,n,m;
ll dis1[maxn],dis2[maxn],vis[maxn],head1[maxn],head2[maxn];
void add(ll bg, ll ed, ll v, EDGE e[],ll head[],ll &cnt)
{
e[++cnt]=EDGE(ed,v,head[bg]);
head[bg]=cnt;
}
void dij(EDGE e[],ll head[],ll dis[])
{
memset(dis,0x3f,sizeof(dis1));
For(i,1,n)vis[i]=0;
priority_queue<PA>q;
dis[1]=0;q.push(mp(0,1));
while(!q.empty())
{
int u=q.top().se;q.pop();
if(vis[u])continue;
vis[u]=1;
for(ll i=head[u];i!=-1;i=e[i].next)
{
if(dis[e[i].to]>dis[u]+e[i].v)
{
dis[e[i].to]=dis[u]+e[i].v;
q.push(mp(-dis[e[i].to],e[i].to));
}
}
}
}
int main()
{
int t;
t=read();
while(t--)
{
cnt1=cnt2=0;
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
n=read(),m=read();
For(i,1,m)
{
ll x,y,v;
x=read(),y=read(),v=read();
add(x,y,v,e1,head1,cnt1);
add(y,x,v,e2,head2,cnt2);
}
dij(e1,head1,dis1);
// For(i,1,n)cout<<dis1[i]<<' ';
// cout<<endl;
dij(e2,head2,dis2);
ll ans=0;
For(i,1,n)ans+=dis1[i]+dis2[i];
write(ans);
putchar('\n');
}
return 0;
}
Heavy Transportation(poj1797)
(最短路的变式)
Sample Input
1
3 3
1 2 3
1 3 4
2 3 5
Sample Output
Scenario #1:
4
题意&&题解:
- 由m条边,求出1到n中需要的边最大(最短路的变式)
- 本题的dis要改成存1到这个点i的最大边权。
- 原本的松弛操作是把大的dis放到队尾,这道题要把大的dis放到队首。
- 本题的输出是要 \n 的 233
反思总结:
- 对于每个点算边权,要看从v到u(u出队节点),如果dis【v】更小,说明从u到v之前有更大的边权,这时就要入队了。
- 优先队列默认大根堆,本题求最大直接入队,(求最小的话,加‘-’,再入队)
- 原本的松弛操作 : d[v]= min(d[v], d[u]+w);
- 本题的松弛操作 : d[v]= max(d[v] , min(d[u], w);
AC(dij)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define fzhead EDGE(int _to, int _v, int _next)
#define fzbody to(_to) ,v(_v) ,next(_next)
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
#define mp make_pair
#define se second
#define fi first
using namespace std;
typedef long long ll;
typedef pair<int,int>pa;
typedef pair<ll,ll>PA;
const int maxn=1e3+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
inline int read()
{
int ans=0;
char ch=getchar();bool neg=false;
while(ch<'0'||ch>'9')
{
if(ch=='-')neg=true;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ans=ans*10+ch-'0';
ch=getchar();
}
return (neg)?-ans:ans;
}
void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
struct EDGE
{
int to,next,v;
EDGE(){}
fzhead : fzbody{}
}e[maxm];
int vis[maxn],dis[maxn],head[maxn];
int cnt,n,m;
void add(int bg, int ed, int v)
{
e[++cnt]=EDGE(ed,v,head[bg]);
head[bg]=cnt;
}
void dij(int s)
{
memset(dis,0,sizeof(dis));//求最大边权,除起点外,其他点初始化为0
For(i,1,n)vis[i]=0;
priority_queue<pa>q;
dis[s]=INF;q.push(mp(dis[s],s));
while(!q.empty())
{
int u=q.top().se;q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u]; i!=-1; i=e[i].next)
{
int v=e[i].to,w=e[i].v;
if(dis[v]<min(dis[u],w))
{
dis[v]=min(dis[u],w);
q.push(mp(dis[v],v));//大的在队首
}
}
}
}
int main()
{
int t,kase=0;
t=read();
while(t--)
{
memset(head,-1,sizeof(head));
cnt=0;
n=read(),m=read();
For(i,1,m)
{
int x=read(),y=read(),v=read();
add(x,y,v);
add(y,x,v);
}
dij(1);
printf("Scenario #%d:\n%d\n\n", ++kase, dis[n]);
// printf("Scenario #%d:\n", ++kase);
//write(ans);
// putchar('\n');
// putchar('\n');
}
return 0;
}
Frogger poj2253(最短路变形)
Sample Input
**2
0 0
3 4
3
17 4
19 4
18 5
0**
Sample Output
Scenario #1
Frog Distance = 5.000
Scenario #2
Frog Distance = 1.414
题意&&题解
有一只青蛙A(1号节点),想跳到青蛙B的石头上(2号节点),但不是直接跳过去,湖中间还有其他石头。
题目要求你跳到b上,找到最短路中的最大的边权(所以这道题的松弛操作时的存路径改为存最长边)
反思总结
- 这道题先算边权,而要用坐标公式,即要用double。
- 在memset的时候我忘记了double不能直接0x3f,一直debug
233
AC(dij)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#define fzhead EDGE(int _to, double _v, int _next)
#define fzbody to(_to), v(_v), next(_next)
#define For(i,x,y) for(register int i=(x);i<=(y);i++)
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline int read()
{
int ans=0;
char ch=getchar();
bool neg=false;
while(ch<'0'||ch>'9')
{
if(ch=='-')neg=false;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ans=ans*10+ch-'0';
ch=getchar();
}
return (neg)?-ans:ans;
}
double ca(int x, int y, int x1, int y1)
{
int m=(x-x1)*(x-x1)+(y-y1)*(y-y1);
return sqrt(double(m));
}
typedef long long ll;
typedef pair<double,int>pa;
const int maxm=1e6+1e3;
const int INF=0x3f3f3f3f3f;
const int maxn=2e2+10;
struct EDGE
{
int to,next;
double v;
EDGE() {}
fzhead:
fzbody{}
} e[maxm];
double dis[maxn];
int vis[maxn],head[maxn],x[maxn],y[maxn];
int n,m,cnt;
void add(int bg, int ed, double v)
{
e[++cnt]=EDGE(ed,v,head[bg]);
head[bg]=cnt;
}
void dij(int s)
{
For(i,1,n)vis[i]=0;
priority_queue<pa>q;
dis[1]=0;
q.push(mp(dis[s],s));
while(!q.empty())
{
int u=q.top().se;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u]; i!=-1; i=e[i].next)
{
int v=e[i].to;
double w=e[i].v;
//cout<<dis[v]<<' '<<v<<endl;
if(dis[v]>max(dis[u],w))
{
dis[v]=max(dis[u],w);
//cout<<dis[v]<<' '<<v<<endl;
q.push(mp(-dis[v],v));
}
}
}
}
int main()
{
int kase=0;
while(scanf("%d", &n)&&n)
{
For(i,1,n)dis[i]=INF;///
cnt=0;
memset(head,-1,sizeof(head));
for(int i=1; i<=n; i++)
x[i]=read(),y[i]=read();
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(i!=j)
{
double d=ca(x[i],y[i],x[j],y[j]);
add(i,j,d);
}
}
}
// cout<<dis[2]<<endl;
//for(int i=1; i<=n; i++)cout<<dis[i]<<' ';
// cout<<endl<<endl;
dij(1);
// for(int i=1; i<=n; i++)cout<<dis[i]<<' ';
// cout<<endl;
printf("Scenario #%d\nFrog Distance = %.3lf\n\n",++kase,dis[2]);
}
return 0;
}
The Shortest Path in NyaGraph(hdu4725)
Sample Input
**2
3 3 3
1 3 2
1 2 1
2 3 1
1 3 3
3 3 3
1 3 2
1 2 2
2 3 2
1 3 4**
Sample Output
Case #1: 2
Case #2: 3
题意&&思路
本题是求最短路,可是本题有了一个条件:就是有层次。
而直接把dujust层的点连边的话,复杂度不允许。
tip:本题可以建立一个层次节点。
总共边数为7n=5n(建立层次节点为本层1n+2n+2n)+2n(m条边)
AC(dij)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define fi first
#define se second
#define mp make_pair
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
#define fzhead EDGE(int _to, int _v, int _next)
#define fzbody to(_to), v(_v), next(_next)
using namespace std;
typedef long long ll;
typedef pair<int,int>pa;
typedef pair<ll,ll>PA;
const int maxm=8e5+10;
const ll INF= 0x3f3f3f3f3f3f3f3f;
const int maxn=2e5+10;
struct EDGE
{
int to,next;
ll v;
EDGE(){}
fzhead:fzbody{}
}e[maxm];
int vis[maxn],head[maxn];
ll dis[maxn],c;
int cnt,n,m;
void add(int bg, int ed, ll v)
{
e[++cnt]=EDGE(ed, v, head[bg]);
head[bg]=cnt;
}
void dij(int s)
{
For(i,1,2*n)vis[i]=0;
mst(dis,0x3f);
priority_queue<PA>q;
dis[s]=0;q.push(mp(dis[s],s));
while(!q.empty())
{
int u=q.top().se;q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u]; i!=-1; i=e[i].next)
{
int v=e[i].to, w=e[i].v;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push(mp(-dis[v],v));
}
}
}
}
int main()
{
int t,kase=0;
scanf("%d", &t);
while(t--)
{
cnt=0;
mst(head,-1);
scanf("%d%d%lld",&n,&m,&c);
int cur;
For(i,1,n)
{
scanf("%d",&cur);
add(n+cur,i,0);//建立层次节点,这个点到本层的距离为0
if(cur+1<=n)add(n+cur+1,i,c),add(i,n+cur+1,c);//这个点到相邻层的距离为c
if(cur-1>=1)add(n+cur-1,i,c),add(i,n+cur-1,c);
}
ll v;
For(i,1,m)
{
int x,y;
scanf("%d%d%lld",&x,&y,&v);
add(x,y,v);
add(y,x,v);
}
dij(1);
// For(i,1,n)cout<<dis[i]<<' ';
// cout<<endl;
printf("Case #%d: ", ++kase);
if(dis[n]==INF)
printf("-1\n");
else
printf("%lld\n", dis[n]);
}
return 0;
}