今天开始做专题四了,刷题的顺序是看某位知乎大佬大佬的回复,现在完成了专题一,希望可以把剩下的做完吧。
[kuangbin带你飞]专题一 简单搜索
[kuangbin带你飞]专题四 最短路练习
[kuangbin带你飞]专题五 并查集
[kuangbin带你飞]专题六 最小生成树
[kuangbin带你飞]专题十二 基础DP1
[kuangbin带你飞]专题十四 数论基础
[kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher
[kuangbin带你飞]专题十七 AC自动机
Till the Cows Come Home POJ - 2387
这道题是求两点最短路问题,我们可以用dijkstra算法求出。其中这里的路径是双向的,还有就是进入一条路后只能走到黑,我这里用了邻接表,就是把每次输入的值都存入到表中,每一个结点i都有一个链表,里面保存着从i出发的所有边。建立邻接表的方法,这里我们调用add()函数,将数据加入到邻接表,交换起点终点再加入到邻接表。
数据都存入邻接表后,我们就开始调用dijkstra()函数,这里采用了优先队列,我们需要重载运算符<,使得队列里小整数先出队,然后我们把起点压入栈,进行队列出队入队,最后得到结果。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
# define LOCAL
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn = 1005;
int num = 0;
int first[maxn];
int u[maxn],v[maxn],e[maxn],next[maxn];
int dir[maxn];
struct node{
int w,u;
};
int operator < (node a,node b){
return a.w > b.w;
}
void add(int x,int y,int z){
u[num]=x,v[num]=y,e[num]=z;
next[num] = first[x];
first[x] = num++;
}//插入链表
void dijkstra(int s){
priority_queue<node> q;
dir[s] = 0;
node tmp;
tmp.u = s,tmp.w = 0;
q.push(tmp);//将初始值压入栈
while(!q.empty()){
node cur = q.top();
q.pop();
int t = cur.u;
int w = cur.w;
if(dir[t] < w)
continue;//如果储存的路径小于当前路径长度,进入下一次循环
for(int i=first[t];i!=-1;i=next[i]){
if(dir[v[i]] > dir[t]+e[i]){//判断是否要更新路径
dir[v[i]] = dir[t]+e[i];
tmp.w = dir[v[i]];
tmp.u = v[i];
q.push(tmp);
}
}
}
}
inline int read(){
char ch=getchar();int f=1,x=0;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
memset(e,inf,sizeof(e));
memset(dir,inf,sizeof(dir));
int t=read(),n=read();
/* memset(first,-1,sizeof(first));
for(int i=0;i<maxn/5;i++)
printf("%d\n",first[i]);*/
for(int i=1;i<=n;i++)//要<=,输入的数据是从1开始的!
first[i] = -1;
for(int i=1;i<=t;i++){
int x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
}
dijkstra(n);
printf("%d\n",dir[1]);
return 0;
}
Frogger POJ - 2253
这道题的话就是找两点的最大路段的最小值,我们所用的方法就是,我们首先用v[i][j]数组存储各个点之间的距离,然后用dis数组记录起点到各个点的距离。先查找与起点最近的点,即跳的最短距离,记录下来,然后再根据这个最近的点,如果起点直接到某个点的距离比通过这个最近点的距离大的话,就更新起点到该点的距离,即可以通过中间点到达该点。因为终点下标是2,所以就直接返回dis[2]。
这里需要提一下的是,double型数据在printf输出的时候,是用%f,因为在printf函数中是没有定义%lf,这个wrong answer卡了我好久。然后scanf是要用%lf。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
# define LOCAL
using namespace std;
const int maxn = 209;
const int inf = 0x3f3f3f3f;
int x[maxn],y[maxn];
double dis[maxn],v[maxn][maxn];//v数组存储两个点之间的距离,dis数组存储第一个点到各个点的路径中最大路段的最小值
bool vis[maxn];//标记走过的点
double dijkstra(int n){
for(int i=1;i<=n;i++)
dis[i] = v[i][1];//初始化
dis[1] = 0;
memset(vis,false,sizeof(vis));
vis[1] = true;
for(int i=1;i<n;i++){
int w = 0;
double nmin = inf;
for(int j=1;j<=n;j++)
if(!vis[j] && nmin > dis[j]){
w = j;
nmin = dis[j];
}//查找从第一个点出发的路径的最大路段的最小值
vis[w] = true;
if(w==2) break;
for(int j=1;j<=n;j++)
if(!vis[j] && dis[j]>max(nmin,v[j][w])){
dis[j] = max(nmin,v[j][w]);
}//更新dis数组
}
return dis[2];//返回终点到起点的最大路段的最小值
}
inline int read(){
char ch=getchar();int f=1,x=0;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
int n,k=1;
while(scanf("%d",&n) && n){
if(k!=1)
printf("\n");
for(int i=1;i<=n;i++){
x[i] = read();
y[i] = read();
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
v[i][j] = v[j][i] = sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));//计算两点的距离
printf("Scenario #%d\n",k++);
printf("Frog Distance = %.3f\n",dijkstra(n));//%lf wrong answer
//(严格地讲,%lf在printf下是未定义的,但是很多系统可能会接受它。要确保可移植性,就要坚持使用%f。)
}
return 0;
}
Heavy Transportation POJ - 1797
这道题求的是载重量最小值最大的一条路,这道题其实和青蛙那道题非常类似,就是求的东西不一样,我们只需要把青蛙那题查找最短距离的改成查找起点到各点的最大载重量,记录下来,然后再根据这个对d数组进行更新,就是如果起点直接到该点的最大载重量小于,查找到的那个点到该点路径的最大载重量,和起点到所找到的那个点的最大载重量,就更新这条路路径,表示起点到这个点还可以通过中间点得到一条载重量最小值更大的路径。
//和青蛙跳石子的问题一样,只是求的反过来了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
# define LOCAL
using namespace std;
const int maxn = 1010;
int v[maxn][maxn],d[maxn];//d[i]表示1到i的所有可能路径中载重量最小值最大的一条路的最小值
bool vis[maxn];//判断是否走过
int dijkstra(int n){
for(int i=1;i<=n;i++)
d[i] = v[1][i];
memset(vis,false,sizeof(vis));
vis[1] = true;
for(int i=1;i<n;i++){
int w = -1;
int nmax = 0;
for(int j=1;j<=n;j++){
if(!vis[j] && d[j]>nmax){
w = j;nmax = d[j];
}//找到载重量最大的路
}
if(w==-1) break;
vis[w] = true;
for(int j=1;j<=n;j++){
if(!vis[j] && d[j]<min(nmax,v[w][j]))//v[w][j]表示从位置w到j
d[j] = min(nmax,v[w][j]);
}//更新到1到其他可能的路径
}
return d[n];//输出
}
inline int read(){
char ch=getchar();int f=1,x=0;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main(){
std::ios::sync_with_stdio(false);
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
int t=read();
for(int i=1;i<=t;i++){
memset(v,0,sizeof(v));
int n=read(),m=read();
for(int j=1;j<=m;j++){
int x=read(),y=read(),z=read();
v[x][y] = v[y][x] = z;//存储路的载重量
}
printf("Scenario #%d:\n",i);
printf("%d\n\n",dijkstra(n));
}
return 0;
}
Silver Cow Party POJ - 3268
这道题其实和昨天做的那道去KFC的搜索题目有些类似,同样是调用两次函数,这里我也和昨天的一样,把两次调用得到的数组存入到一个数组里,就是加一维空间,上下层存放。这道题的大意是说奶牛去派对并回来,其中去的路线和回来的路线不能相同,需要找到每一头奶牛的最短路程,然后输出奶牛最多要走多远。我们这里也是调用dijkstra函数,需要注意的是,所给的路径是单向的,所以我们再找回来的路的时候需要将矩阵转置,再调用dijkstra函数。其中这里的dijkstra函数和前面的很类似,所以就不细讲了。
这里我犯了一个很低级的错误,就是在转置的时候,转置了两次,结果又转回去了,我们只需要转置下半部分的矩阵就可以实现矩阵转置。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
# define LOCAL
using namespace std;
const int maxn = 1010;
const int inf = 0x3f3f3f3f;
bool vis[maxn];//判断是否出现过
int low_cost[2][maxn];//第一维没有交换的各点到x的值,第二维放交换后的
int dijkstra(int n,int x,int flag,int cost[][maxn]){
memset(vis,false,sizeof(vis));
low_cost[flag][x] = 0;
for(int i=1;i<=n;i++){
int w=-1,nmin = inf;
for(int j=1;j<=n;j++)
if(!vis[j] && nmin > low_cost[flag][j])
nmin = low_cost[flag][w=j];//找到花费最少的路径
if(w==-1)
break;
vis[w] = true;
for(int j=1;j<=n;j++)
if(!vis[j] && low_cost[flag][j] > low_cost[flag][w]+cost[w][j])
low_cost[flag][j] = low_cost[flag][w]+cost[w][j];//更新其他路径
}
}
inline int read(){
char ch=getchar();int f=1,x=0;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int cost[maxn][maxn];
int main(){
std::ios::sync_with_stdio(false);
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
int n,m,x;
while(scanf("%d%d%d",&n,&m,&x)==3){
memset(low_cost,inf,sizeof(low_cost));//初始化
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==j)
cost[i][j] = 0;
else
cost[i][j] = inf;
}//初始化
while(m--){
int u=read(),v=read(),e=read();
cost[u][v] = min(cost[u][v],e);
}//读入数据
dijkstra(n,x,0,cost);
/* for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cout << cost[i][j] << " ";
cout << endl;
}
cout << endl;
for(int i=1;i<=n;i++)
cout << low_cost[0][i] << endl;
cout << endl;*/
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)//swap交换只用换一般,再换就恢复原样
swap(cost[i][j],cost[j][i]);
/* for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cout << cost[i][j] << " ";
cout << endl;
}*/
dijkstra(n,x,1,cost);
/* for(int i=1;i<=n;i++)
cout << low_cost[1][i] << endl; */
int ans = 0;
for(int i=1;i<=n;i++)
ans = max(ans,low_cost[0][i]+low_cost[1][i]);
printf("%d\n",ans);
}
return 0;
}