题目一:
涉及知识点: 正反图的最短路
题意:
有N个农场 , 每个农场一头牛 , 现在他们要走到第X个农场去参加派对 , 然后返回各自的农场。
然后有M条单向路 , 每条路从A到B花费T
然后每头牛都会选择最短的路来走一个来回
求的是每头牛走的路中的最大值
思路:
图论水题 , 很裸的最短路算法。
算法采用spfa , 构造正反两个图 , 我们知道如果从x点开始走 , low[i]记录的就是从x出发到达i的最短距离。
那么去的时候采用正图,回的时候采用反图就可以了
代码如下:
#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const long long LLinf = 1e30;
int n,m,x;
int vis[1010];
int G[1010][1010] , G2[1010][1010];
int low[1010],low2[1010];
void init()
{
memset(vis,0,sizeof(vis));
for(int i = 0 ; i <= n ; i++)
{
low[i] = INF,low2[i] = INF;
for(int j = 0 ; j <= n ; j++)
{
G[i][j] = G2[i][j] = INF;
}
}
}
void spfa(int src , int *low2 , int g[][1010])
{
memset(vis , 0 , sizeof(vis));
queue<int> que;
que.push(src);
low2[src] = 0;
vis[src] = 1;
while(!que.empty())
{
int cur = que.front();
que.pop();
vis[cur] = 0;
for(int i = 1 ; i <= n ; i++)
if(low2[cur]+g[cur][i] < low2[i])
{
low2[i] = low2[cur]+g[cur][i];
if(!vis[i])
{
que.push(i);
vis[i]=1;
}
}
}
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&x))
{
init();
int a,b,t;
for(int i = 0 ; i < m ; i++)
{
scanf("%d%d%d",&a,&b,&t);
G[a][b] = min(G[a][b] , t);
G2[b][a] = min(G2[b][a] , t);
}
spfa(x , low , G);
spfa(x , low2 , G2);
int maxc = -1;
for(int i = 1 ; i <= n ; i++)
{
if(low[i]!= INF && low2[i] != INF)
maxc = max(low[i]+low2[i] , maxc);
}
cout << maxc << endl;
}
return 0;
}
题目二
题意:
很丢脸地没看懂题。。。百度的中文翻译,说是从1到所有点的最短路和加上所有点到1的最短路和,图是有向图
思路:
和上题如出一辙,于是无脑敲代码瞬间1Y了 , 就当是复习了一下邻接表
代码如下
#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const long long LLinf = 1e30;
int N;
struct edge
{
int now;
int next;
int w;
}e[1000000+10];
struct node
{
int x,y,t;
}Node[1000000+10];
int cnt=0;
int head[1000000+10];
int low[1000000+10];
int vis[1000000+10];
void add(int x , int y , int v)
{
e[cnt].now = y;
e[cnt].next = head[x];
e[cnt].w = v;
head[x] = cnt++;
}
LL sum;
int P,Q;
void spfa(int src)
{
memset(vis , 0 , sizeof(vis));
for(int i = 0 ; i <= P ; i++)
low[i] = INF;
low[src]=0;
vis[src]=1;
queue<int> que;
que.push(src);
while(!que.empty())
{
int cur = que.front();
que.pop();
vis[cur]=0;
for(int i = head[cur] ; i!= -1 ; i = e[i].next)
{
int now = e[i].now;
if(low[now] > low[cur] + e[i].w)
{
low[now] = low[cur] + e[i].w;
if(!vis[now])
{
que.push(now);
vis[now] =1;
}
}
}
}
}
int main()
{
scanf("%d" , &N);
while(N--)
{
sum=0;
scanf("%d%d" , &P , &Q);
memset(head , -1 , sizeof(head));
for(int i = 0 ; i < Q ; i++)
{
scanf("%d%d%d" , &Node[i].x,&Node[i].y,&Node[i].t);
add(Node[i].x,Node[i].y,Node[i].t);
}
spfa(1);
for(int i = 2 ; i <= P ; i++)
sum += low[i];
cnt=0;
memset(head , -1 , sizeof(head));
for(int i = 0 ; i < Q ; i++)
add(Node[i].y,Node[i].x,Node[i].t);
spfa(1);
for(int i = 2 ; i <= P ; i++)
sum += low[i];
cout << sum << endl;
}
return 0;
}
题目三:
涉及知识点:不同起点的最短路,也可用最小生成树做
题意:
直接讲输入:
第一行一个数n,是总共有n个人
后面每一行的第一个数k:和k个人有单向关系;然后k对数,表示(有关系的人的编号,传输信息时间)
求从哪个人开始能够以最短时间使信息传遍所有人,输出这个人的编号和最短时间,始终不能传遍所有人输出dis...
思路:
对于一个人,首先在能传遍所有人的情况下,记到达某个人所花时间为tt,那么求tt肯定是按照最短时间去走的,最短路就是用在这了,求出的所有tt中的最大值就是传遍所有人需要的最短时间ttt。然后求出所有人的ttt判断哪个最小就可以了。
判断dis的方法:
我利用了low这个数组,如果在spfa结束后low[1]-low[n]中有inf,说明他没被更新过,是不可达的。
代码如下:
#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int G[110][110];
int vis[110];
int low[110];
int n;
int spfa(int src)
{
memset(vis,0,sizeof(vis));
memset(low , 0x1f , sizeof(low));
vis[src]=1;
queue<int> que;
que.push(src);
low[src] = 0;
while(!que.empty())
{
int cur = que.front();
que.pop();
vis[cur]=0;
for(int i = 1 ; i <= n ; i++)
{
if(low[i] > low[cur] + G[cur][i])
{
low[i] = low[cur]+G[cur][i];
if(!vis[i])
{
vis[i]=1;
que.push(i);
}
}
}
}
if(*max_element(low+1 , low+n+1) == inf)
return -1;
//low[src] = inf;
return *max_element(low+1 , low+n+1);
}
int main()
{
while(~scanf("%d",&n)&&n)
{
memset(G , 0x1f , sizeof(G));
for(int i = 1 ; i <= n ; i++)
{
int nn;
scanf("%d" , &nn);
for(int j = 0 ; j < nn ; j++)
{
int to,w;
scanf("%d%d" , &to , &w);
G[i][to] = w;
}
}
int res = inf;
int pos=-1;
for(int i = 1 ; i <= n ; i++)
{
int ans = spfa(i);
if(ans != -1 && ans < res)
res = ans,pos = i;
}
if(res == inf)
printf("disjoint\n");
else
printf("%d %d\n" , pos , res);
}
return 0;
}
题目四:
涉及知识点:最短路变形,最小生成树变形
题意:
从1到n有若干段路,每条路的最大载重为w,在所有从1到n可达的路中找出一条路,使得能运送的货物最重,比如从1到n的其中一条路有3段路,最大载重为1,2,3,则最多能运送重为1的货物,即一个木桶原理模型。
思路:
这题我是用最小生成树变体水过的,思路很简单:肯定是要求选取的路中,每段路载重尽可能大,那么就相当于找一棵最大生成树,当然我们不必找全,从1出发,当n加入到已选点的集合时,就可以退出了
最小生成树代码如下:
#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int G[1010][1010];
int vis[1010];
int low[1010];
int n,m;
int prim()
{
int minc = inf;
vis[1]=1;
for(int i = 2 ; i <= n ; i++)
low[i] = G[1][i];
for(int i = 2 ; i <= n ; i++)
{
int maxc = -1;
int pos=-1;
for(int j = 1 ; j <= n ; j++)
if(!vis[j] && low[j] > maxc)
{
maxc = low[j];
pos=j;
}
vis[pos]=1;
minc = min(minc,maxc);
if(pos == n)
return minc;
for(int j = 1 ; j <= n ;j++)
if(!vis[j] && low[j] < G[pos][j])
low[j] = G[pos][j];
}
return minc;
}
int main()
{
int t;
scanf("%d" , &t);
for(int ka = 1 ; ka <= t ; ka++)
{
memset(vis,0,sizeof(vis));
memset(low , 0 , sizeof(low));
memset(G,0,sizeof(G));
scanf("%d%d" , &n,&m);
for(int i = 0 ; i < m ; i++)
{
int x,y,w;
scanf("%d%d%d" , &x , &y , &w);
G[x][y] = G[y][x] = w;
}
printf("Scenario #%d:\n%d\n\n",ka,prim());
}
return 0;
}
后来补了个最短路的,代码如下:
#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int G[1010][1010];
int vis[1010];
int low[1010]; //此时low表示从1到i的路段中最短路段的载重量
int n,m;
int dijkstra()
{
int minc = inf;
vis[1]=1;
for(int i = 2 ; i <= n ; i++)
low[i] = G[1][i];
for(int i = 2 ; i <= n ; i++)
{
int maxc = -1;
int pos=-1;
for(int j = 1 ; j <= n ; j++)
if(!vis[j] && low[j] > maxc)
{
maxc = low[j];
pos=j;
}
vis[pos]=1;
for(int j = 1 ; j <= n ;j++)
if(!vis[j] && low[j] < min(G[pos][j],maxc)) //这里的maxc就是从1到pos的路段中最大的载重量
low[j] = min(G[pos][j],maxc);
}
return low[n];
}
int main()
{
int t;
scanf("%d" , &t);
for(int ka = 1 ; ka <= t ; ka++)
{
memset(vis,0,sizeof(vis));
memset(low , 0 , sizeof(low));
memset(G,0,sizeof(G));
scanf("%d%d" , &n,&m);
for(int i = 0 ; i < m ; i++)
{
int x,y,w;
scanf("%d%d%d" , &x , &y , &w);
G[x][y] = G[y][x] = w;
}
printf("Scenario #%d:\n%d\n\n",ka,dijkstra());
}
return 0;
}
题目五:
题意:
没怎么仔细读就上手做了,一次CE小错误改正之后就过了,那估计题目是没理解错。
说是有一堆石头,两只青蛙在1,2两块石头上,1要通过其他石头跳到2。称从一块石头跳到另一块为一次跳跃,而每条路程中,一定会有一个跳跃距离是最大的,问所有最大跳跃的最短距离是多少。
思路:和上一题如出一辙,dijkstra变形水过
代码:
#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
double G[210][210];
double low[210]; //这次是表示从1—i的最大跳跃距离
int vis[210];
int x[210],y[210];
int n;
double dis(int x1,int y1,int x2,int y2)
{
return sqrt(1.0*(x1-x2)*(x1-x2) + 1.0*(y1-y2)*(y1-y2));
}
double dij()
{
int ans = inf;
for(int i = 2 ; i <= n ; i++)
{
low[i] = G[1][i];
}
for(int i = 2 ; i<= n ; i++)
{
int minc = inf;
int pos = -1;
for(int j = 1 ; j <= n ; j++)
{
if(!vis[j] && low[j] < minc) //在未收入的点集中寻找最小的 "最大跳跃距离"
{
minc = low[j];
pos = j;
}
}
vis[pos]=1;
ans = min(ans,minc);
for(int j = 1 ; j <= n ; j++)
{
if(!vis[j] && low[j] > max(G[pos][j] , low[pos])) //更新当前最大跳跃距离
{
low[j] = max(G[pos][j] , low[pos]);
}
}
}
return low[2];
}
int main()
{
int t=0;
while(~scanf("%d",&n) && n)
{
memset(G,0x1f,sizeof(G));
memset(low,0x1f,sizeof(low));
memset(vis,0,sizeof(vis));
low[1]=0;
vis[1]=1;
for(int i = 1 ; i <= n ; i++)
{
scanf("%d%d",&x[i],&y[i]);
for(int j = 1 ; j < i ; j++)
{
G[i][j] = G[j][i] = dis(x[i],y[i],x[j],y[j]);
}
}
printf("Scenario #%d\nFrog Distance = %.3lf\n\n",++t,dij());
}
return 0;
}
题目四和五,一个是最小中找最大,一个是最大中找最小,千万熟记机理以及过程!!
题目六:
中文题,题意略
思路:
一种物品在另一种物品的影响下会改变其价格
仅考虑3种物品:
我从1到3要10000元,但是如果有2,2是100元,1的价格就会变为200,也就是说1->2->3的价值和要比1->3小,我们显然会选前者。
从上例可看出这是一个典型的松弛操作,于是自然就想到用最短路解决。
建图:
G[i][j]表示在i的影响下j的价格,那么如果没有物品影响呢?找一个点(这里用0),G[0][i]表示i的初始价格,搜图从此点出发
松弛操作的限制条件:
就是等级限制了,这里是一群交易人的最大lv和最小lv之差不能超过m,而且酋长肯定是在其中的。
但是如果直接限定范围:[lv-m , lv+m] (注意酋长等级不一定是最大的),区间长度为2m肯定不可取,所以我们要以他为中心,枚举所有可能的等级区间[lv-m + i , lv + i]
代码如下:
其实写得挺挫的,根本没有必要保留一堆物品信息,仅保留等级就可以了
#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int m,n;
int G[110][110];
int vis[110];
int low[110];
struct mono
{
int price;
int lv;
int X;
int replace[110][2];
}item[110];
void read()
{
scanf("%d%d",&m,&n);
memset(G,0x1f,sizeof(G));
for(int i = 1 ; i <= n ; i++)
{
scanf("%d%d%d",&item[i].price,&item[i].lv,&item[i].X);
G[0][i] = item[i].price; //G[0][i]表示毫无优惠下的价格
for(int j = 1 ; j <= item[i].X ; j++)
{
scanf("%d%d" , &item[i].replace[j][0] , &item[i].replace[j][1]);
G[item[i].replace[j][0]][i] = item[i].replace[j][1];
}
}
}
int dij(int l,int r)
{
memset(vis,0,sizeof(vis));
memset(low,0x1f,sizeof(low));
vis[0]=1;
low[0]=0;
for(int i = 1 ; i <= n ; i++)
low[i] = G[0][i];
for(int i = 1 ; i <= n ; i++)
{
int minc=inf;
int pos;
for(int j = 0 ; j <= n ; j++)
{
if(item[j].lv >= l && item[j].lv <= r && !vis[j] && minc > low[j])
{
minc = low[j];
pos = j;
}
}
vis[pos]=1;
if(pos == 1)
break;
for(int j = 0 ; j <= n ; j++)
{
if(item[j].lv >= l && item[j].lv <= r && !vis[j] && low[j] > G[pos][j] + low[pos])
low[j] = G[pos][j] + low[pos];
}
}
return low[1];
}
int main()
{
read();
int minc = inf;
for(int i = item[1].lv-m >0 ? item[1].lv-m:0 ; i <= item[1].lv ; i++)
minc = min(minc , dij(i,i+m));
printf("%d\n",minc);
return 0;
}
题目七:
题意:
有K元钱,要从1-N,每条路都有一定长度,要付一定的钱,求在K元范围内到达N的最短距离
思路:
带有限制条件的最短路,具体注意点在代码里了,这里提几点:
1,使用优先队列,首先挑最短的,其次挑钱最少的,这很明显,所以挑出来的只要在K元内,只要能到N,必定就是当前路径长度最短的
2,注意是单向图...................坑了两个小时
代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int K,N,R;
int S,D,L,T;
struct edge
{
int now;
int next;
int len;
int value;
}e[10010];
struct node
{
int id;
int len;
int coin;
node(int x,int y,int c)
{id = x , len = y , coin = c;}
bool operator <(const node& a)const
{
if(len == a.len)
return coin > a.coin;
return len>a.len;
}
};
int head[250] , vis[250];
int cnt=0;
int low[250];
void add(int x,int y,int len,int v)
{
e[cnt].len = len;
e[cnt].value = v;
e[cnt].now = y;
e[cnt].next = head[x];
head[x] = cnt++;
}
int dij()
{
low[1] = 0;
priority_queue<node> que;
que.push(node(1,0,0));
while(!que.empty())
{
node cur = que.top();
que.pop();
low[cur.id] = cur.len; //同一个id在队列里会有不同的状态,所以现在取出来的不一定就是最近更新的那一个状态
//即状态回溯了
//这句必不可少,而下面循环中的更新就可以不要了
if(cur.id == N) //到N了立刻退出,因为出队的方式是路径短的优先,如果不及时跳出,
return low[N]; //后面可能会有同样到达N但路径必定长于当前的方案覆盖当前方案
for(int i = head[cur.id] ; i != -1 ; i = e[i].next)
{
int now = e[i].now;
node tmp(now , cur.len + e[i].len , cur.coin+ e[i].value); //下一个待定点的参数
if(cur.coin+ e[i].value <= K)
{
//low[now] = low[cur.id] + e[i].len ; //如果符合条件,更改最短路径值
que.push(tmp);
}
}
}
if(low[N] != inf)
return low[N];
return -1;
}
int main()
{
scanf("%d%d%d" , &K,&N,&R);
memset(head,-1,sizeof(head));
memset(low,0x1f,sizeof(low));
for(int i = 1 ; i <= R ; i++)
{
scanf("%d%d%d%d" , &S,&D,&L,&T);
add(S,D,L,T);
}
printf("%d\n",dij());
return 0;
}
题目八:
题意:
输入数据:
N M S V
货币种类数 货币间转换方法数 当前拥有哪一种货币 有多少这种货币
A B RAB CAB RBA CBA
货币A,货币B A到B的汇率 A到B的手续费 B到A的汇率 B到A的手续费
...............
比如最初有20元A货币,A到B的转换汇率为1.10 , 手续费1.00 那么能得到B货币(20-1.00)*1.10元
最后问 能否经过一系列给定的转换使得最后换到A时钱数增加了
思路:
就是一个找环 , 找环的话本渣渣一般就想到两个:拓扑和最短路找负环
这里拓扑肯定不行,因为双向边会有冲突 , 那么就是最短路找负环了,而能处理负环的,掌握的最好的就是spfa了,好了,就是它了。
图的结点就是货币,权值就是汇率和手续费
其实就是把spfa模板改改,判定仍旧是一个点如果多次进入队列就表明有环,不过这里找的是正环
low[i]表示i货币当前能够拥有的最大数量
于是程序如下:
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int N,M,S;
double V;
double G[110][110][2];
int vis[110];
double low[110];
int cnt[110];
int spfa()
{
vis[S]=1;
low[S] = V;
queue<int> que;
que.push(S);
while(!que.empty())
{
int cur = que.front();
que.pop();
vis[cur]=0;
for(int i = 1 ; i <= N ; i++)
{
if(low[i] < (low[cur] -G[cur][i][1])*G[cur][i][0])
{
low[i] = (low[cur] -G[cur][i][1])*G[cur][i][0];
if(!vis[i])
{
vis[i]=1;
que.push(i);
cnt[i]++;
}
}
}
if(cnt[S] >= N)
return 1;
}
if(low[S] > V)
return 1;
return 0;
}
int main()
{
//freopen("Input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
memset(vis,0,sizeof(vis));
memset(low,0,sizeof(low));
memset(G,0,sizeof(G));
memset(cnt,0,sizeof(cnt));
scanf("%d%d%d%lf" , &N,&M,&S,&V);
for(int i = 0 ; i < M ; i++)
{
int a,b;
double x,y,u,v;
scanf("%d%d%lf%lf%lf%lf" , &a,&b,&x,&y,&u,&v);
G[a][b][0] = x , G[a][b][1] = y;
G[b][a][0] = u , G[b][a][1] = v;
}
cout << (spfa() ? "YES" : "NO") << endl;
return 0;
}
题目九
题意:
给你一群货币的名字和从A货币到B货币的汇率 , 判断是否有一种货币会在若干次汇率转换之后使自身的钱数增加
思路:
做过上一题后这题意图就很明显了,仍旧spfa找环,这里要注意的就是数据的读入和处理了,我是给每一种货币都编号,用map容器来映射编号
注意:
本题的所有建成的图都是连通图(AC后改了程序测试的,题面并没有说明),所以从第一个开始一次spfa就可以搞定了,但如果图不是连通的,那就要另外处理了,我这里反正这题数据小,就直接每个点爆枚了,数据量大,就得按照连通分量来减少处理次数。
代码:
#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
map<string , int> lis;
double G[50][50];
int vis[50];
double low[50];
int cnt[50];
int n;
int spfa(int src)
{
memset(vis , 0 , sizeof(vis));
memset(low,0,sizeof(low));
memset(cnt , 0 , sizeof(cnt));
queue<int> que;
que.push(src);
vis[src]=1;
low[src]=100;
while(!que.empty())
{
int cur = que.front();
vis[cur]=0;
que.pop();
for(int i = 1 ; i <= n ; i++)
{
if(low[i] < low[cur] * G[cur][i])
{
low[i] = low[cur] * G[cur][i];
if(!vis[i])
{
vis[i]=1;
que.push(i);
cnt[i]++;
if(cnt[i] > n)
return 1;
}
}
}
}
return 0;
}
int main()
{
//freopen("Input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
int Case=1;
while(cin >> n && n)
{
lis.clear();
memset(G , 0 , sizeof(G));
string line;
for(int i = 1 ; i <= n ;i++)
{
cin >> line;
lis[line] = i;
}
int nn;
cin >> nn;
for(int i =1 ; i<= nn ; i++)
{
string l1,l2;
double change;
cin >> l1 >> change >>l2;
G[lis[l1]][lis[l2]] = change;
}
printf("Case %d: ",Case++);
int ok=0;
for(int i = 1 ; i<=n ; i++)
{
ok = spfa(1);
if(ok)
break;
}
ok ? puts("Yes") : puts("No");
}
return 0;
}
题目十:
题意:给了一个图的邻接矩阵,x代表两点之间不可直接到达,求从1到其余每个点最短时间的最长时间
思路:裸最短路,只不过输入有点恶心
代码:
#include <iostream>
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int n;
int G[110][110];
int low[110];
int vis[110];
int spfa()
{
queue<int> que;
que.push(1);
vis[1] = 1;
low[1] = 0;
while(!que.empty())
{
int cur = que.front();
que.pop();
vis[cur] = 0;
for(int i = 1 ; i <= n ; i++)
{
if(low[i] > low[cur] + G[cur][i])
{
low[i] = low[cur] + G[cur][i];
if(!vis[i])
{
vis[i]=1;
que.push((i));
}
}
}
}
return *max_element(low + 1 , low+n+1);
}
int main()
{
//freopen("Input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d" , &n))
{
memset(vis,0,sizeof(vis));
memset(low,0x1f,sizeof(low));
memset(G,0x1f,sizeof(G));
for(int i = 2 ; i <= n ; i++)
{
char Get[100];
for(int j = 1 ; j < i ; j++)
{
scanf("%s" , Get);
if(Get[0] == 'x')
continue;
else
G[i][j] = G[j][i] = atoi(Get);
}
}
printf("%d\n" , spfa());
}
return 0;
}
题目十一:
题意:
正确的句子的定义:
0.里面仅包含p~z ,N,C,D,E,I (我觉得题面应该改一下,把这句里面的language改成sentence才不会误导别人)
1.每一个单个的p~z是一个正确句子
2.如果s是正句,那么Ns也是
3.如果s,t是正句,那么Cst,Dst,Est,Ist也是
4.0~3是唯一判断依据
现在给你一个句子,判断其是否是正确句子
思路:
从前向后遍历,不管如何,遇到N,是一句变一句,句子数量不增加,遇到其余四个大写字母,是两句变一句,句子数量减1,遇到小写字母,句子增加1
最后看看句子数是不是1就行了
YY的思路,还没有严密的证明。。。。。不过还真过了
代码如下:
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int main()
{
//freopen("Input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
string line;
while(cin >> line)
{
int juzi=0;
for(int i = 0 ; i < line.size() ; i++)
{
if(line[i] >= 'p' && line[i] <= 'z')
juzi++;
else if(line[i] == 'C' || line[i] == 'D' || line[i] == 'E' || line[i] == 'I')
juzi--;
else if(line[i] == 'N')
;
else
{
juzi = 0;
break;
}
}
cout << ((juzi == 1) ? "YES" : "NO" )<< endl;
}
return 0;
}
题目十二:
题意:
给起点和终点的两个坐标(输入第一行),接下来是一堆列车的站点,每一条线路都有至少两个站点,以坐标形式给出站点,(-1,-1) 结束一条线路站点的输入,
乘车是40km/h,走路10km/h 给的数据都是以m为单位的,最后求的是从起点到终点,最快几分钟能走完,注意是分钟
思路:
以每个站点和起终点为节点建图,边权:如果属于同一条线路则是SubwayTime,否则WalkTime,结构体中用id表示这个站点处于哪条线路
注意点:
1.单位转换
2.重边
3.一条线路,相邻两站才能以subwayTime可达,比如1->2->3 , 如果线路不是一条直线的话,1要到3则需经过2,才能用subwayTime计算
4.坑了我2小时的一个点,全怪我英语渣。。注意最后答案取的是最接近的一个整数,而不是向上或者向下取整
代码如下:
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
struct node
{
int x;
int y;
int id;
}p[210];
int n = 2;
double G[210][210];
double low[210];
int vis[210];
double dis(node a,node b)
{
return sqrt(1.0*(a.x-b.x)*(a.x-b.x) + 1.0*(a.y-b.y)*(a.y-b.y));
}
double subwayTime(double dis)
{
return dis/1000.0 / 40.0*60.0;
}
double walkTime(double dis)
{
return dis/1000.0/10.0*60.0;
}
double spfa()
{
queue<int> que;
que.push(1);
vis[1]=1;
low[1]=0;
while(!que.empty())
{
int cur = que.front();
que.pop();
vis[cur]=0;
for(int i = 1 ; i <= n ; i++)
{
if(low[i] > low[cur] + G[cur][i])
{
low[i] = low[cur] + G[cur][i];
if(!vis[i])
{
vis[i]=1;
que.push(i);
}
}
}
}
return low[2];
}
int main()
{
//freopen("Input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
scanf("%d%d%d%d" , &p[1].x , &p[1].y , &p[2].x , &p[2].y);
memset(G , 127 , sizeof(G));
memset(low,127,sizeof(low));
int a,b;
p[1].id=1 , p[2].id=2;
int onaji=3;
while(~scanf("%d%d" , &a,&b))
{
if(a == -1 && b == -1)
{
onaji++;
continue;
}
n++;
p[n].x = a , p[n].y = b , p[n].id = onaji;
if(p[n-1].id == p[n].id)
G[n-1][n] = G[n][n-1] = min(G[n-1][n] , subwayTime( dis(p[n-1],p[n]) ));
for(int i = 1 ; i <= n ; i++)
G[i][n] = G[n][i] = min(G[i][n] , walkTime( dis(p[i],p[n]) ));
}
cout << int(spfa()+0.5) << endl;
return 0;
}