昨天一天才把DJ&SPFA卷明白,麻了麻了 2021-7-15
三天了,各种最短路板子终于敲熟了,入门(√)。 2021-7-17
PS:进阶好难www
2021年暑训最短路专题
Problem A:Poj2387
Til the Cows Come Home
题目大意:已知一个有T条边,N个点的无向图,求从点1到点N的最短路(板子题)
题解:数据量不大,DJ可以不需要优化,当点的数据超过1e4时最好采用堆优化DJ。
AC代码:(堆优化DJ)
#include<iostream>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int maxn=2e3+7;
const int inf=0x3f3f3f3f;
struct edge{
int to,cost;
};
vector<edge> G[maxn];
int d[maxn];
int n,t;
priority_queue<P,vector<P>,greater<P> > que;
void dij(int s){
fill(d+1,d+1+n,inf);
d[s]=0;
que.push(P(0,s));
while(!que.empty()){
P p=que.top();
que.pop();
int v=p.second;
if(d[v]<p.first)continue;
for(int i=0;i<G[v].size();i++){
edge e=G[v][i];
if(d[e.to]>d[v]+e.cost) {
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to],e.to));
}
}
}
}
int main(){
cin>>t>>n;
for(int i=1;i<=t;i++){
int a,b,c;
cin>>a>>b>>c;
edge e;
e.to=b,e.cost=c;
G[a].push_back(e);
e.to=a;
G[b].push_back(e);
}
dij(1);
cout<<d[n];
return 0;
}
Problem B:Poj2253
Frogger
题目大意:青蛙A在第一块石头,青蛙B在第二块石头,旁边还有其他石头,给出每块石头的坐标,青蛙A有很多种方案到达第二块石头,它想知道所有路径中最大的最短距离。
题解:(堆优化DJ)这道题难在无法理解如何松弛,在求最短路的时候,d[x]数组保存的是从源点到达点x路径中最大的最短距离,由于是求最短距离,
我们的堆要是小顶堆,这样才能保证每次取出的路径是最短的,在此基础上对每个端点进行松弛(d[i]=min(d[i],max(d[v],e.cost))),尽量让最短的边达到最大。
AC代码:
#include<iostream>
#include<vector>
#include<queue>
#include<math.h>
#include<iomanip>
using namespace std;
typedef pair<int,double> P;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
struct edge{
int to;
double cost;
};
vector<edge>G[maxn];
int n;
double x[202],y[202];//石块坐标
double d[maxn];
void dij(int s){
fill(d+1,d+1+n,inf);
d[s]=0;
priority_queue<P,vector<P>,greater<P> >que;//小根堆
que.push(P(0,s));
while(!que.empty()){
P p=que.top();
que.pop();
int v=p.second;
if(d[v]<p.first)continue;
for(int i=0;i<G[v].size();i++){
edge e=G[v][i];
if(d[e.to]>max(d[v],e.cost)){//松弛操作//针对于inf,将d[x]=inf转化为max(d[v],e.cost)
d[e.to]=max(d[v],e.cost);
que.push(P(d[e.to],e.to));//处理完所有点后得到的d[n]就是我们要求的最大的最小值
}
}
}
}
int main(){
int cnt=1;
while(cin>>n&&n){
for(int i=1;i<=202;i++)G[i].clear();
for(int i=1;i<=n;i++){
cin>>x[i]>>y[i];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
double dis=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
edge e;
e.to=j;
e.cost=dis;
G[i].push_back(e);
}
}//邻接表存图
dij(1);
cout<<"Scenario #"<<cnt++<<endl<<"Frog Distance = ";
cout<<fixed<<setprecision(3)<<d[2]<<endl<<endl;
}
}
Problem C: Poj1797
Heavy Transportation
题目大意:与B类似,求所有路径中最小的最大值。
题解:与B类似,d[x]数组初始化为0,d[s]初始化为inf,优先队列选用大根堆,松弛方程为d[e.to]=max(d[e.to],min(d[v],e.cost));
AC代码:
#include<iostream>
#include<vector>
#include<queue>
#include<stdio.h>
using namespace std;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
typedef pair<int,int>P;
int t,n,m;
struct edge {
int to,cost;
};
vector<edge> G[maxn];
int d[maxn];
void dj(int s){
fill(d+1,d+1+n,0);
d[s]=inf;
priority_queue<P,vector<P>,less<P> >que;
que.push(P(inf,s));
while(!que.empty()){
P p=que.top();
que.pop();
int v=p.second;
if(d[v]<p.first)continue;
for(int i=0;i<G[v].size();i++){
edge e=G[v][i];
if(d[e.to]<min(d[v],e.cost)){
d[e.to]=min(d[v],e.cost);
que.push(P(d[e.to],e.to));
}
}
}
}
int main(){
scanf("%d",&t);
int cnt=1;
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
edge e;
e.to=a,e.cost=c;
G[b].push_back(e);
e.to=b;
G[a].push_back(e);
}
dj(1);
printf("Scenario #%d:\n%d\n\n",cnt++,d[n]);
for(int i=1;i<=n;i++)G[i].clear();
}
return 0;
}
Problem D: Poj3268
Silver Cow Party
题目大意:N个农场,M条路(单向边),其中编号为X的农场要举行party,求其他农场的cow们中往返时间最长的那只的往返时间。
题解:自己画张图很容易证明a到b的最短路,可以由b到a所求最短路的矩阵的转置所求出。所以本题两次Dj即可完成。
AC代码:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int>P;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
struct edge{
int to,cost;
};
vector<edge>G1[maxn],G2[maxn];//两张图,G2是G1的转置
int n,m,x,d1[maxn],d2[maxn];//d1和d2分别是G1和G2图中所求最短路
void dj1(int s){//G1的最短路
fill(d1+1,d1+1+n,inf);
d1[s]=0;
priority_queue<P,vector<P>,greater<P> >que;
que.push(P(0,s));
while(!que.empty()){
P p=que.top();
que.pop();
int v=p.second;
if(d1[v]<p.first)continue;
for(int i=0;i<G1[v].size();i++){
edge e=G1[v][i];
if(d1[e.to]>d1[v]+e.cost){
d1[e.to]=d1[v]+e.cost;
que.push(P(d1[e.to],e.to));
}
}
}
}
void dj2(int s){//G2的最短路
fill(d2+1,d2+1+n,inf);
d2[s]=0;
priority_queue<P,vector<P>,greater<P> >que;
que.push(P(0,s));
while(!que.empty()){
P p=que.top();
que.pop();
int v=p.second;
if(d2[v]<p.first)continue;
for(int i=0;i<G2[v].size();i++){
edge e=G2[v][i];
if(d2[e.to]>d2[v]+e.cost){
d2[e.to]=d2[v]+e.cost;
que.push(P(d2[e.to],e.to));
}
}
}
}
int main(){
cin>>n>>m>>x;
for(int i=1;i<=m;i++){//邻接表存图
int a,b,c;
cin>>a>>b>>c;
edge e;
e.to=b,e.cost=c;
G1[a].push_back(e);
e.to=a;
G2[b].push_back(e);
}
dj1(x);
dj2(x);
int ans=0;
for(int i=1;i<=n;i++){//找出所有cow中花费时间最长的那只
ans=max(ans,d1[i]+d2[i]);
}
cout<<ans;
}
Problem E Poj1860
Currency Exchange
题目大意:已知你是Nike,你家附近有M台货币转换机,可以转换N种货币,你目前有S种类的货币V枚,每台货币转换机具有六个数据:a,b为两种货币的类型,Cab、Rab是a转化为b的汇率及佣金,Cba、Rba是b转化为a的汇率及佣金。
a转化为b的转化公式:设a有k枚,那么可以转化(k-Rab)*Cab枚b。
问:是否有可能在转化(不限次数)后,使自己本金变多?
题解:由于需要增加本金,那么我们需要在这些转换规则中找到一个正环,
利用SPFA解决问题。
AC代码:
#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stack>
#include<map>
#include<limits.h>
#include<vector>
#include<string.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pii;
const int N = 1e7;
const int M = 1e5+7;
#define INF 1e7+5
#define INM INT_MIN
#define pb(a) push_back(a)
#define mk(a,b) make_pair(a,b)
#define dbg(x) cout << "now this num is " << x << endl;
#define sd(a) scanf("%d",&a)
#define sld(a) scanf("%lld",&a)
#define sdd(a,b) scanf("%d %d",&a,&b)
#define sddd(a,b,c) scanf("%d %d %d",&a,&b,&c)
#define pr(a) printf("%d\n",a)
#define plr(a) printf("%lld\n",a)
#define pr_(a) printf("%d ",a)
#define _pr(a) printf(" %d",a)
int vis[105],n,m,s;
double dis[105],v;
struct Node
{
int to;
double a1,b1;//存转换规则,只存A-B的,因为看成有向边
};
vector<Node> G[105];
bool spfa(int x)
{
memset(vis,0,sizeof(vis));
vis[x] = 1;
for(int i=1;i<=n;++i) dis[i] = 0;//这里应该是找最大路
dis[x] = v;
queue<int> Q;
Q.push(x);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = 0;//消除标记
for(int i=0;i<G[u].size();++i)
{
Node e = G[u][i];
if(dis[e.to] < (dis[u]-e.b1)*e.a1)
{
dis[e.to] = (dis[u]-e.b1)*e.a1;
if(!vis[e.to])
{
vis[e.to] = 1;//添加标记
Q.push(e.to);
}
}
if(dis[x] > v) return true;//判断是否存在正环了
}
}
return false;
}
int main()
{
sddd(n,m,s);
scanf("%lf",&v);
while(m--)
{
int a,b;
double a1,b1,a2,b2;
sdd(a,b);
scanf("%lf %lf %lf %lf",&a1,&b1,&a2,&b2);
Node p,q;
p.to = b;p.a1 = a1;p.b1 = b1;
q.to = a;q.a1 = a2;q.b1 = b2;
G[a].pb(p);
G[b].pb(q);
}
if(spfa(s)) printf("YES\n");
else printf("NO\n");
}
Problem F: Poj3259
Wormholes
题目大意:一个农夫有N块田,M条路(双向),每条路需要花费一定时间,他的田中还存在W个虫洞,这些虫洞可以将农夫传送回一定时间以前(单向),问:有没有一种方案能使他回到初始点与自己碰面。
题解:虫洞的时间为负,SPFA判断是否存在负环即可。
AC代码:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int>P;//first为点,second为记录迭代了多少次
const int maxn=5e2+7;
const int inf=0x3f3f3f3f;
int d[maxn];
int n,m,w;
int vis[maxn];
struct edge{
int to,cost;
};
vector<edge>G[maxn];
bool SPFA(int s){
int cnt[maxn]={0};
fill(d+1,d+1+n,inf);
fill(vis+1,vis+1+n,0);
d[s]=0;
vis[s]=1;
queue<P>que;
que.push(P(s,cnt[s]));
while(!que.empty()){
P p=que.front();
que.pop();
int v=p.first;
vis[v]=0;//点出队,成环时用得到
//if(p.second>n)return 0;
for(int i=0;i<G[v].size();i++){//判断与V相邻的点
edge e=G[v][i];
if(d[e.to]>d[v]+e.cost){//松弛
d[e.to]=d[v]+e.cost;
if(!vis[e.to]){
que.push(P(e.to,++cnt[e.to]));
vis[e.to]=1;
}
}
}
for(int i=1;i<=n;i++){
if(cnt[i]>n)return 0;
}
}
return 1;
}
int main(){
int t;
cin>>t;
while(t--){
for(int i=1;i<=maxn;i++)G[i].clear();
cin>>n>>m>>w;
for(int i=1;i<=m;i++){
int a,b,c;
edge e;
cin>>a>>b>>c;
e.to=b,e.cost=c;
G[a].push_back(e);
e.to=a;
G[b].push_back(e);
}
for(int i=1;i<=w;i++){
int a,b,c;
edge e;
cin>>a>>b>>c;
e.to=b,e.cost=-c;
G[a].push_back(e);
}
if(SPFA(1)){
cout<<"NO"<<endl;
}
else cout<<"YES"<<endl;
}
return 0;
}
Problem G: Poj1502
MPI Maelstrom
题目大意:已知N个点,给出一个下三角矩阵(由于是无向图,只需要给出下三角矩阵即可),“X”代表无法到达,问从1到其他点最短路中最长的距离是多少。(本题建图很有意思,感兴趣的同学可以自己建一手)
题解:1<=n<=100,由于数据量很弱,选择用Floyd来写,DJ和SPFA都可以写但没必要。Floyd写完遍历源点到其他点的距离取最大值。
AC代码:
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
const int maxn=2e5+7;
const int inf=0x3f3f3f3f;
int e[101][101];
int main(){
int n;
char s[11];
cin>>n;
for(int i = 1;i<=n;i ++){//初始化
for(int j = 1;j<=n;j ++){
if(i == j)
e[i][j] = 0;
else
e[i][j] = inf;
}
}
for(int i = 2;i<=n;i ++){
for(int j = 1;j<i;j ++){
scanf("%s",s);
if(strcmp(s,"x") == 0)
e[i][j] = inf;
else
e[i][j] = e[j][i] = atoi(s);//atoi()函数可以将字符串自动转化为数字
}
}
for(int k=1;k<=n;k++){//经典Floyd
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
}
}
}
int ans=0;
for(int i=1;i<=n;i++){//取最大值
ans=max(ans,e[1][i]);
}
cout<<ans;
}
Problem H: Poj3660
Cow Contest
题目大意:有N头牛,M个关系,每个关系输入A,B,表示A能打败B,问最多能确定多少头牛的排名。
题解:关键的一点就是:只要一头牛最终的关系网中出现了其他四头牛,那么这头牛的排名便可以确定。1<=n<=100,我们还是可以用Floyd解决问题,将每头牛之间的关系完善(比如A>B&&B>C那么A>C)。
AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m,a,b;
int G[101][101]={0};
int num[101]={0};
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {//A打败B,G[a][b]置为1
cin >> a >> b;
G[a][b] = 1;
}
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (G[i][k] == 1 && G[k][j] == 1) {只有i打败k,k打败j,i才可以打败j
G[i][j] = 1;
}
}
}
}
int ans=0;
for(int i=1;i<=n;i++){//选择一头牛
int cnt=0;
for(int j=1;j<=n;j++){//查看关系网
if(G[i][j]||G[j][i])cnt++;
}
if(cnt==n-1)ans++;//出现了n-1头牛ans++
}
cout<<ans;
}
Problem I:Poj2240
Arbitrage
题目大意:已知N种货币和M种兑换关系,问第一种货币是否可以在经过交易后变多。
题解:将汇率当成边权,Floyd跑一边后看第一个货币经过转换后的汇率是否大于一即可。(又是一个有意思的建图)
AC代码:
#include<iostream>
#include<map>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m;
double G[100][100];
map<string,int> name;
int main() {
int cnt=1;
while(cin>>n){
if(n==0)break;
string s[50];
for(int i=1;i<=n;i++){
cin>>s[i];
name[s[i]]=i;
}
cin>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j)G[i][j]=1;
else G[i][j]=0;
}
}
for(int i=1;i<=m;i++){
string a,b;
double rat;
cin>>a>>rat>>b;
G[name[a]][name[b]]=rat;
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(G[i][j]<G[i][k]*G[k][j]){//因为要找汇率更大的货币,判断三种汇率之间的大小关系取更大者
G[i][j]=G[i][k]*G[k][j];
}
}
}
}
int flag=0;
for(int i=1;i<=n;i++){
if(G[i][i]>1){
flag=1;
break;
}
}
if(flag)cout<<"Case "<<cnt++<<": Yes"<<endl;
else cout<<"Case "<<cnt++<<": No"<<endl;
}
}
Problem J:Poj1511
Invitation Cards
题目大意:与Problem B几乎相同,仅仅加了一层n,m的多组输入。
题解:由于数据量较大并且存在多组输入,建议使用链式前向星存图,再使用转置矩阵加双DJ。(板子题就不多说了)
AC代码:(这里我用的是Vector模拟邻接表的,7000ms差点超时)
#include<cstdio>
#include<string.h>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#define maxn 1000001
#define inf 0x3f3f3f3f
typedef pair<int,int> P;
struct edge{
int f;
int t;
int c;
edge(){ f=0,t=0,c=0;}
edge(int ff,int tt,int cc){
f=ff,t=tt,c=cc;
}
};
int dist[maxn];
vector<edge> map[maxn];
edge edges[maxn];
void dijkstra(int s,int n){
priority_queue<P,vector<P>,greater<P> > Q;
for(int i=1;i<=n;i++)
dist[i]=inf;
dist[s]=0;
bool visited[maxn];
memset(visited ,0,sizeof(visited));
Q.push(P(0,s));
while(!Q.empty()){
int v=Q.top().second;
Q.pop();
if(visited[v]) continue;
visited[v]=true;
for(int i=0;i<map[v].size();i++){
edge e=map[v][i];
if(dist[e.t]>dist[v]+e.c){
dist[e.t]=dist[v]+e.c;
Q.push(P(dist[e.t],e.t));
}
}
}
}
void init(int n){
for(int i=0;i<=n;i++){
map[i].clear();
}
}
int main(){
//freopen("in.txt","r",stdin);
int cases;
scanf("%d",&cases);
for(int t=1;t<=cases;t++){
int n,m;
scanf("%d %d",&n,&m);
init(n);
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
edges[i]=edge(a,b,c);
map[a].push_back(edge(a,b,c));
}
dijkstra(1,n);
long long int sum=0;
for(int i=1;i<=n;i++)
sum+=dist[i];
init(n);
for(int i=1;i<=m;i++){
edge tmp=edges[i];
map[tmp.t].push_back(edge(tmp.t,tmp.f,tmp.c));
}
dijkstra(1,n);
for(int i=1;i<=n;i++)
sum+=dist[i];
printf("%lld\n",sum);
}
}
Problem K:Poj3159
Candies
题目大意:N个孩子,M个关系,输入a,b,c代表b的糖果数不会比a多c个以上。(差分约束题)
题解:差分约束板子题,由于求两个孩子间最大的糖果数之差,那我们用SPFA跑一遍最短路。
差分约束不懂的同学请移步至差分约束系统
AC代码:(链式前向星+SPFA)
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=3e4+7;
int head[150009];
int vis[maxn];
int dis[maxn];
struct edge{
int to,cost,next;
}e[150009];
int t;
int n,m,u,v,w;
void addedge(int v,int w){
e[t].to=v;
e[t].cost=w;
e[t].next=head[u];
head[u]=t++;
}
void SPFA(int s){
fill(dis+1,dis+1+n,inf);
fill(vis+1,vis+1+n,0);
stack<int>q;
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty()){
int v=q.top();
q.pop();
vis[v]=0;
for(int i=head[v];~i;i=e[i].next){
if(dis[e[i].to]>dis[v]+e[i].cost){
dis[e[i].to]=dis[v]+e[i].cost;
if(!vis[e[i].to]){
vis[e[i].to]=1;
q.push(e[i].to);
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
t=0;
fill(head,head+1+n,-1);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
addedge(v,w);
}
SPFA(1);
int mx=0;
for(int i=2;i<=n;i++){
mx=max(mx,dis[i]);
}
printf("%d",mx);
return 0;
}
Problem L: Poj2502
Subway
题目大意:给你家、学校的坐标,以及几条地铁线,每个地铁线有几个地铁站,给出地铁站的坐标,在地铁线上可以以40km/h的速度行驶,从家到地铁站,从地铁线到地铁线,从地铁站到学校,步行速度为10km/h,问最少多久能从家到学校。(坐标以m为单位)。
题解:难在建图上,每个点之间都要建一条边,建完图后就是一个DJ或者SPFA的板子题了。
AC代码:
#include<vector>
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<set>
#include<cmath>
#include<cstring>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<double,int> pii;
typedef pair<int,pii> PII;
const int inf = 1<<30;
const int maxn = 1e5+5;
struct Node
{
int x,y;
Node(){}
Node(int a,int b):x(a),y(b){}
};
Node beg,en;
vector<Node> node;
Node D[maxn];
struct Edge
{
int from,to;
double val;
double cost;
};
vector<Edge> G[maxn];
double getval(Node &a,Node& b)
{
return sqrt((double)(abs(a.x-b.x)*abs(a.x-b.x)+abs(a.y-b.y)*abs(a.y-b.y)));
}
double getcost(double cnt,bool issubway)
{
double ans = 0;
if(issubway){
ans = cnt/40000 * 60;
}else{
ans = cnt/10000 * 60;
}
return ans;
}
double dis[maxn];
bool vis[maxn];
int cnt = 0;
double dijkstra()
{
memset(vis,0,sizeof(vis));
fill(dis,dis+maxn,inf);
priority_queue<pii,vector<pii>,greater<pii> > Q;
Q.push(pii(0,cnt));
while(!Q.empty()){
pii t = Q.top();
Q.pop();
int u = t.second;
double d = t.first;
if(vis[u])continue;
vis[u] = 1;
for(int i=0;i<G[u].size();i++){
Edge e = G[u][i];
if(e.cost + d < dis[e.to]){
dis[e.to] = e.cost + d;
Q.push(pii(dis[e.to],e.to));
}
}
}
return dis[cnt+1];
}
int main(int argc, char const *argv[])
{
scanf("%d%d",&beg.x,&beg.y);
scanf("%d%d",&en.x,&en.y);
int x,y;
int k = 0;
Edge e;
while(~scanf("%d%d",&x,&y)){
if(x==-1&&y==-1){
for(int i=k;i<cnt-1;i++){
e.val = getval(D[i], D[i+1]);
e.cost = getcost(e.val, 1);
e.from = i;
e.to = i+1;
G[e.from].push_back(e);
swap(e.from, e.to);
G[e.from].push_back(e);
}
k = cnt;
continue;
}
D[cnt] = Node(x,y);
cnt++;
}
D[cnt] = beg;
D[cnt+1] = en;
for(int i=0;i<cnt+2;i++){
for(int j=i+1;j<cnt+2;j++){
e.val = getval(D[i], D[j]);
e.cost = getcost(e.val, 0);
e.from = i;
e.to = j;
G[e.from].push_back(e);
swap(e.from, e.to);
G[e.from].push_back(e);
}
}
double ans = dijkstra();
printf("%d\n",(int)(ans+0.5));
return 0;
}
Problem M: Poj1062
昂贵的聘礼
题目大意:中文题就不需要大意了吧?自己看去欸嘿嘿。
题解:将两件物品之间的转换关系当作权值建图,由于不知道酋长等级,我们枚举每一个可能的等级跑DJ,找到花费最小的那条路。
PS:还有一个建立超级源点的方法,后面学完了会补上来的。
AC代码:
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<math.h>
#include<string.h>
#define re register
using namespace std;
typedef long long ll;
const double eps=1e-7;
const int INF=1e9;
const int N=105;
int n,m;
int x,y;
int cnt;
int tot;
int a,c;
int dis[N];
bool vis[N];
int level[N];
int mp[N][N];
int dij(int l,int r)
{
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
dis[0]=0;
// vis[0]=1;
for(int i=1;i<=n+1;i++)
{
int t=-1;
for(int j=0;j<=n;j++)
if(!vis[j]&&(t==-1||dis[t]>dis[j])) t=j;
vis[t]=1;
for(int j=1;j<=n;j++)
if(level[j]>=l&&level[j]<=r)
dis[j]=min(dis[j],dis[t]+mp[t][j]);
}
return dis[1];
}
int main()
{
memset(mp,0x3f,sizeof mp);
cin>>m>>n;
for(int i=1;i<=n;i++)
{
int val,cnt;
cin>>val>>level[i]>>cnt;
mp[0][i]=min(mp[0][i],val);
for(int j=1;j<=cnt;j++)
{
int id,w;
cin>>id>>w;
mp[id][i]=min(mp[id][i],w);
}
}
int ans=INF;
for(int i=level[1]-m;i<=level[1];i++) ans=min(ans,dij( i, i+m));
cout<<ans;
return 0;
}
Problem N: Poj1847
Tram
题目大意:N个岔路口,我们要从a到b,每个岔路口会给x条路,分别指向对应的岔路口,铁轨初始状态默认打在第一条路上,问最少要操作几次铁轨才能到达?
题解:DJ\SPFA板子题,建图时每个岔路口第一条铁轨默认权值为0,其他的为1,跑一遍DJ\SPFA即可。
AC代码:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
//typedef pair<int,int>P;
const int inf=0x3f3f3f3f;
const int maxn=1e2+7;
int n,a,b;
int dis[maxn];
int vis[maxn];
int G[maxn][maxn];
void dij(int s){
fill(dis+1,dis+1+n,inf);
fill(vis+1,vis+1+n,0);
dis[s]=0;
while(1){
int v=-1;
for(int i=1;i<=n;i++){
if(!vis[i]&&(v==-1||dis[v]>dis[i]))v=i;
}
if(v==-1)break;
vis[v]=1;
for(int i=1;i<=n;i++){
if(dis[i]>dis[v]+G[v][i]){
dis[i]=dis[v]+G[v][i];
}
}
}
}
int main() {
cin>>n>>a>>b;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j)G[i][j]=0;
else G[i][j]=inf;
}
}
for(int i=1;i<=n;i++){
int x;
cin>>x;
for(int j=1;j<=x;j++){
int y;
cin>>y;
if(j==1)G[i][y]=0;
else G[i][y]=1;
}
}
dij(a);
if(dis[b]>=inf)cout<<"-1"<<endl;
else cout<<dis[b]<<endl;
}
Problem O: LightOJ1704
Extended Traffic
题目大意:N座城市,每个城市会给出拥挤度Pi,ab两座城市之间花费是(Pb-Pa)^3,问从x到y花费最少的时间是多少。如果不能到达或者时间小于等于3,输出“?”,否则输出所需时间。
题解:SPFA判负环(板子题)。
AC代码:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int>P;
const int inf=0x3f3f3f3f;
const int maxn=2e2+7;
int t,n,m,q;
int busy[202];
struct edge{
int to,cost;
};
vector<edge>G[maxn];
int d[maxn];
int vis[maxn];
int cnt[maxn]={0};
void SPFA(int s){
queue<int>q;
fill(d+1,d+1+n,inf);
fill(vis+1,vis+1+n,0);
d[s]=0;
q.push(s);
vis[s]=1;
while(!q.empty()){
int v=q.front();
q.pop();
vis[v]=0;
for(int i=0;i<G[v].size();i++){
edge e=G[v][i];
if(d[e.to]>d[v]+e.cost){
d[e.to]=d[v]+e.cost;
if(!vis[v]&&cnt[e.to]<=n){
vis[e.to]=1;
q.push(e.to);
cnt[e.to]++;
}
}
}
}
}
int main(){
cin>>t;
int sum=0;
while(t--){
sum++;
for(int i=1;i<=n;i++)G[i].clear();
fill(cnt+1,cnt+1+n,0);
cin>>n;
for(int i=1;i<=n;i++)cin>>busy[i];
cin>>m;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
edge e;
e.to=b;
e.cost=(busy[b]-busy[a])*(busy[b]-busy[a])*(busy[b]-busy[a]);
G[a].push_back(e);
}
SPFA(1);
cin>>q;
cout<<"Case "<<sum<<":"<<endl;
while(q--){
int x;
cin>>x;
if(d[x]!=inf&&d[x]>=3&&cnt[x]<=n)cout<<d[x]<<endl;
else cout<<"?"<<endl;
}
}
return 0;
}
Problem P:
The Shortest Path in Nya Graph
待补全
Problem Q:
Problem R:
0 or 1
待补全
Problem S: Poj3169
Layout
题目大意:N个人,ML个喜欢规则,MD个讨厌规则,如果无法建立这个图输出-2,如果第一个人和第N个人之间距离为无穷大则输出-1,否则输出第一个人与第N个人之间的距离。
题解:1、构造差分约束系统:
A,B距离不超过D则B-A<=D,
A,B距离至少为D则A-B<=-D.
2、若有解则求1和N之间的最短路径:
例如:A-B<=D1 , B-C<=D2, A-C<=D3 不等式相加得:A-C<=min(D3,D1+D2)。而当A-B<=D时,我们建的边是B->A的,所以我们只要求出1到N的最短距离即可,如果没有最短距离,则输出-2.
AC代码:
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e3+7;
int n,ml,md,a,b,c;
int dis[maxn];
int vis[maxn];
int head[20005];
int cnt[maxn];
struct edge{
int to,cost,next;
}e[20005];
int t;
void addedge(int u,int v,int w){
e[t].to=v;
e[t].cost=w;
e[t].next=head[u];
head[u]=t++;
}
int SPFA(int s){
fill(dis,dis+1+n,inf);
fill(vis,vis+1+n,0);
fill(cnt,cnt+1+n,0);
stack<int>q;
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty()){
int v=q.top();
q.pop();
vis[v]=0;
cnt[v]++;
if(cnt[v]>n)return -1;
for(int i=head[v];~i;i=e[i].next){
if(dis[e[i].to]>dis[v]+e[i].cost){
dis[e[i].to]=dis[v]+e[i].cost;
if(!vis[e[i].to]){
vis[e[i].to]=1;
q.push(e[i].to);
}
}
}
}
if(dis[n]==inf)return -2;
else return dis[n];
}
int main(){
cin>>n>>ml>>md;
fill(head,head+1+n,-1);
t=0;
for(int i=1;i<=ml;i++){
cin>>a>>b>>c;
addedge(a,b,c);
}
for(int i=1;i<=md;i++){
cin>>a>>b>>c;
addedge(b,a,-c);
}
cout<<SPFA(1)<<endl;
return 0;
}
待补全