复习 [kuangbin带你飞]专题4 最短路

1. poj 2387 Til the Cows Come Home

  • 做过了,好像是个裸最短路
#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;
const int MAXN = 2e6 + 100;
typedef long long ll;
struct st{
    int ID;
    ll value;
    friend bool operator < (st x, st y){
        return x.value > y.value;
    }
    st(){}
    st(int ID, ll value){
        this->ID = ID;
        this->value = value;
    }
};
struct Edge{
    int next;//next edge
    int to;// next ver
    ll value;
}edge[MAXN];
int cnt = 0;
int head[MAXN];
int vis[MAXN];
ll dis[MAXN];
void Add_Edge(int u, int v, ll value){
    edge[cnt].value = value;
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    head[u] = cnt++;
}
void dijstra(int n, int s){
    priority_queue<st> q;
    st now;
    dis[s] = 0;
    now.ID = s;
    now.value = dis[s];
    q.push(now);
    while(!q.empty()){
        st u = q.top();
        q.pop();
        if(vis[u.ID]) continue;
        vis[u.ID] = 1;
        for(int i=head[u.ID];i != -1;i = edge[i].next){
            if(!vis[edge[i].to] && dis[u.ID] + edge[i].value < dis[edge[i].to]){
                dis[edge[i].to] = dis[u.ID] + edge[i].value;
                now.ID = edge[i].to;
                now.value = dis[edge[i].to];
                q.push(now);
            }
        }
    }
}
int main(){
    int n, m, s, u, v;
    ll w;
    scanf("%d%d", &m, &n);
    memset(head, -1, sizeof head);
    for(int i=0;i<=n;i++) dis[i] = 2147483647;
    for(int i=0;i<m;i++){
        scanf("%d%d%lld", &u, &v, &w);
        Add_Edge(u, v, w);
        Add_Edge(v, u, w);
    }
    dijstra(n, 1);
    printf("%lld", dis[n]);
    return 0;
}

2. poj 2253 Frogger

  • 这么小的n,是个啥题啊
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
struct NODE{
    int x, y;
}node[MAXN];
struct Edge{
    int to;
    int next;
    double val;
}edge[MAXN];
struct st{
    int ID;
    double value;
    friend bool operator<(st x, st y){
        return x.value > y.value;
    }
};
int cnt = 0;
int head[MAXN];
int vis[MAXN];
int path[MAXN];
double dis[MAXN];
void Add_Edge(int u, int v, double w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].val = w;
    head[u] = cnt++;
}
void dijstra(int s){
    priority_queue<st> q;
    st now;
    dis[s] = 0;
    now.ID = s;
    now.value = dis[s];
    q.push(now);
    while(!q.empty()){
        st u = q.top();
        q.pop();
        if(vis[u.ID]) continue;
        vis[u.ID] = 1;
        for(int i=head[u.ID];i != -1;i=edge[i].next){
            if(max(dis[u.ID], edge[i].val) < dis[edge[i].to] && !vis[edge[i].to]){
                dis[edge[i].to] = max(edge[i].val, dis[u.ID]);
                path[u.ID] = edge[i].to;
                now.ID = edge[i].to;
                now.value = dis[edge[i].to];
                q.push(now);
            }
        }
    }
}
int main(){
    int n, x, y;
    int num = 0;
    while(cin >> n && n){
        for(int i=0;i<n;i++){
            cin >> node[i].x >> node[i].y;
        }
        memset(head, -1, sizeof head);
        memset(vis, 0, sizeof vis);
        memset(path, -1, sizeof path);
        for(int i=0;i<=n;i++){
            dis[i] = 999999999;
        }
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                double DIS = sqrt(1.0 * (node[i].x - node[j].x) * (node[i].x - node[j].x) + 1.0 * (node[i].y - node[j].y) * (node[i].y - node[j].y));
                Add_Edge(i, j, DIS);
                Add_Edge(j, i, DIS);
            }
        }
        dijstra(0);
        int p = 0;
        printf("Scenario #%d\n", ++num);
        printf("Frog Distance = %.3f\n\n", dis[1]);
    }
    return 0;
}

3. poj 1797 Heavy Transportation

  • 还是练手速
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e3 + 100;
const double eps = 1e-6;
struct Edge{
    int next;
    int to;
    int val;
}edge[200000];
struct st{
    int ID;
    int val;
    bool operator<(st x)const{
        return val < x.val;
    }
};
int cnt = 0;
int head[MAXN];
int dis[MAXN];
int vis[MAXN];
void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].val = w;
    head[u] = cnt++;
}
void dijstra(int s){
    priority_queue<st> q;
    st now;
    dis[s] = INF;
    now.ID = s;
    now.val = dis[s];
    q.push(now);
    while(!q.empty()){
        st u = q.top();
        q.pop();
        if(vis[u.ID]) continue;
        vis[u.ID] = 1;
        for(int i=head[u.ID];i != -1;i=edge[i].next){
            if(min(dis[u.ID], edge[i].val) > dis[edge[i].to] && ! vis[edge[i].to]){
                dis[edge[i].to] = min(edge[i].val, dis[u.ID]);
                now.ID = edge[i].to;
                now.val = dis[edge[i].to];
                q.push(now);
            }
        }
    }
}
int main(){
    int t, n, m, u, v, w;
    scanf("%d", &t);
    for(int i=1;i<=t;i++){
        scanf("%d%d", &n, &m);
        memset(vis, 0, sizeof vis);
        memset(dis, -0x3f, sizeof dis);
        memset(head, -1, sizeof head);
        cnt = 0;
        while(m--){
            scanf("%d%d%d", &u, &v, &w);
            Add_Edge(u, v, w);
            Add_Edge(v, u, w);
        }
        dijstra(1);
        printf("Scenario #%d:\n", i);
        printf("%d\n\n", dis[n]);
    }
    return 0;
}

4. poj 3268 Silver Cow Party

  • 好像是正反跑两遍最短路就好了
#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 1e3 + 100;
struct st{
	int ID;
	int val;
	bool operator<(st x)const {
		return val > x.val;
	}
};
struct Edge{
	int next;
	int to;
	int val;
}edge[200000];
int head[MAXN];
int vis[MAXN];
int dis[MAXN];
int cnt;
void Add_Edge(int u, int v, int w){
	edge[cnt].next = head[u];
	edge[cnt].to = v;
	edge[cnt].val = w;
	head[u] = cnt++;
}
map<int, int> mp;
void dijstra(int s){
	priority_queue<st> q;
	st now;
	dis[s] = 0;
	now.ID = s;
	now.val = dis[s];
	q.push(now);
	while(!q.empty()){
		st u = q.top();
		q.pop();
		if(vis[u.ID]) continue;
		vis[u.ID] = 1;
		for(int i=head[u.ID];i!=-1;i=edge[i].next){
			if(!vis[edge[i].to] && dis[u.ID] + edge[i].val < dis[edge[i].to]){
				dis[edge[i].to] = dis[u.ID] + edge[i].val;
				now.ID = edge[i].to;
				now.val = dis[edge[i].to];
				q.push(now);
			}
		}
	}
} 
int main(){
	int n, m, x, u, v, w;
	scanf("%d%d%d", &n, &m, &x);
	memset(head, -1, sizeof head);
	memset(dis, 0x3f, sizeof dis);	
	while(m--){
		scanf("%d%d%d", &u, &v, &w);
		Add_Edge(u, v, w);
	}
	dijstra(x);
	for(int i=1;i<=n;i++){
		mp[i] = dis[i];
	}
	int ans = -0x3f3f3f3f;
	for(int i=1;i<=n;i++){
		if(i == x) continue;
		memset(dis, 0x3f, sizeof dis);
		memset(vis, 0, sizeof vis);
		dijstra(i);
		ans = max(ans, mp[i] + dis[x]);
	}
	cout << ans;
	return 0;
}

5. poj 1860 Currency Exchange

  • 如果某边能够被一直松弛,那么说明能够不停增大
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
double edge1[200][200];
double edge2[200][200];
double dis[200];
bool Bellman_Ford(int n){
    double d[200];
    for(int i=1;i<=n;i++){
        d[i] = dis[i];
    }
    for(int k=1;k<n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(dis[j] < (dis[i] - edge2[i][j]) * edge1[i][j]){
                    dis[j] = (dis[i] - edge2[i][j]) * edge1[i][j];
                }
            }
        }
    }
    for(int i=1; i<=n; i++){
        if(d[i] < dis[i]) return true;
    }
    return false;
}
int main(){
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);
    int n, m, s, u, x, y;
    double v;
    double rab, rba, cab, cba;
    scanf("%d%d%d%lf", &n, &m, &s, &v);
    dis[s] = v;
    while(m--){
        scanf("%d%d%lf%lf%lf%lf", &x, &y, &rab, &cab, &rba, &cba);
        edge1[x][y] = rab;
        edge1[y][x] = rba;
        edge2[x][y] = cab;
        edge2[y][x] = cba;
    }
    Bellman_Ford(n);
    if(Bellman_Ford(n)) printf("YES");
    else printf("NO");
    return 0;
}

6. poj 3259 Wormholes

  • 问能不能在出发之前返回,就是边取正还是负的问题,跑出来如果发现更小了那就说明可以
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
int edge[600][600];
void Floyd(int n){
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            if(edge[i][k] != INF)
            for(int j=1;j<=n;j++){
                if(edge[i][j] > edge[i][k] + edge[k][j]){
                    edge[i][j] = edge[i][k] + edge[k][j];
                }
            }
        }
    }
}
bool check(int n){
    for(int i=1;i<=n;i++){
        if(edge[i][i] < 0) return true;
    }
    return false;
}
int main(){
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);
    int f, n, m, w, s, e, t;
    cin >> f;
    while(f--){
        scanf("%d%d%d", &n, &m, &w);
        memset(edge, 0x3f, sizeof edge);
        for(int i=0;i<=n;i++){
            edge[i][i] = 0;
        }
        for(int i=0;i<m;i++){
            scanf("%d%d%d", &s, &e, &t);
            if(edge[s][e] > t){
                edge[s][e] = edge[e][s] = t;
            }
        }
        for(int i=0;i<w;i++){
            cin >> s >> e >> t;
            edge[s][e] = min(edge[s][e], -t);
        }
        Floyd(n);
        if(check(n)) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}

7. poj 1502 MPI Maelstrom

  • 我的天我真没看见哪里写了从1号节点出发,然后每次走完都可以让走过的节点继续传递,问把所有节点遍历到的最小时间
  • 这题有点像kuangbin MST练习中的一道题。这题就是bellman-ford,从1号节点出发,对其余 n − 1 n-1 n1个点进行 n n n次松弛操作,取所有点所需要时间的最大值即为答案
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 105;
int edge[N][N], dis[N];
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  memset(edge, 0x3f, sizeof edge);
  for(int i=2;i<=n;i++){
    for(int j=1;j<i;j++){
      string s;
      cin >> s;
      if(s == "x") continue;
      int sz = s.length();
      int now = 0;
      for(int k=0;k<sz;k++){
        now *= 10;
        now += s[k] - '0';
      }
      edge[i][j] = edge[j][i] = now;
    }
  }
  memset(dis, 0x3f, sizeof dis);
  dis[1] = 0;
  for(int k=1;k<=n;k++){
    for(int i=1;i<=n;i++){
      for(int j=1;j<=n;j++){
        dis[j] = min(dis[j], dis[i] + edge[i][j]);
      }
    }    
  }
  int ans = 0;
  for(int j=1;j<=n;j++){
    ans = max(ans, dis[j]);
  }
  cout << ans;
  return 0;
}

8. poj 3660 Cow Contest

  • 给你若干组 u , v u,v u,v关系,表示 u u u打败了 v v v,问能确定排名的奶牛个数
  • 一个想法是如果能够确定一只奶牛和其他 n − 1 n-1 n1只奶牛的相对关系,那么这只奶牛的排名就能够确定,基于这个想法,我们设 g [ u ] [ v ] = 1 g[u][v]=1 g[u][v]=1表示 u u u打败 v v v g [ u ] [ v ] = − 1 g[u][v]=-1 g[u][v]=1表示 u u u v v v打败,那么可以使用 f l o y d floyd floyd求出整个图之间的对应关系,然后枚举每个点进行判断即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 105;

const int INF = 0x3f3f3f3f;
int edge[N][N];
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  memset(edge, 0x3f, sizeof edge);
  for(int i=0;i<m;i++){
    int u, v;
    cin >> u >> v;
    edge[u][v] = 1;
    edge[v][u] = -1;
  }
  for(int k=1;k<=n;k++){
    for(int i=1;i<=n;i++){
      for(int j=1;j<=n;j++){
        if(edge[i][k] == edge[k][j] && edge[i][k] != INF){
          edge[i][j] = edge[i][k];
        }
      }
    }
  }
  int ans = 0;
  for(int i=1;i<=n;i++){
    int num = 0;
    for(int j=1;j<=n;j++){
      if(edge[i][j] != INF) num += 1;
    }
    if(num == n - 1) ans += 1;
  }
  cout << ans;
  return 0;
}

9. poj 2240 Arbitrage

  • 给出一些货币之间的兑换比例,问能不能通过某些方式兑换到更多的钱
  • 就是问图里有没有正环,那么显然可以使用floyd,货币之间的关系为乘积,初始自己兑换自已一定是1:1,如果跑完发现某个货币自己兑换自己大于1,那就说明能
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;

int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  int kase = 1;
  while(cin >> n && n){
    map<string, int> mp;
    int cnt = 0;
    for(int i=0;i<n;i++){
      string s;
      cin >> s;
      mp[s] = ++cnt;
    }
    int m;
    cin >> m;
    vector<vector<double> > g(cnt + 1, vector<double>(cnt + 1));    
    bool ok = false;
    for(int i=1;i<=cnt;i++) g[i][i] = 1;
    for(int i=1;i<=m;i++){
      string s1, s2;
      double x;
      cin >> s1 >> x >> s2;
      g[mp[s1]][mp[s2]] = x;
    }
    for(int k=1;k<=cnt;k++){
      for(int i=1;i<=cnt;i++){
        for(int j=1;j<=cnt;j++){
          g[i][j] = max(g[i][j], g[i][k] * g[k][j]);
        }
      }
    }
    for(int i=1;i<=cnt;i++) if(g[i][i] > 1) ok = true;
    cout << "Case " << kase << ": " << (ok ? "Yes" : "No") << '\n';
    kase += 1;
  }
  return 0;
}

10. poj 1511 Invitation Cards

  • 1 0 6 10^6 106个点,问从1到其余点的最短路和从其余各点到1的最短路之和
  • 显然正着跑一边 d i j k s t r a dijkstra dijkstra再建反图跑一遍 d i j k s t r a dijkstra dijkstra即可,这么多点,链式前向星比较保险
  • 说好的 1 0 9 10^9 109怎么用 i n t int int是错的
  • 好久没遇到卡 s c a n f scanf scanf的了,也就poj, hdu这些毒瘤机能干出这事来,关了流同步, c i n cin cin还是奇慢
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 1e6 + 10;
int n, m;
struct Edge{
  int next;
  int to;
  int val;
}edge1[N << 1], edge2[N << 1];
int head1[N], head2[N], dis1[N], dis2[N];
bool vis1[N], vis2[N];
int cnt1, cnt2;
void Add_Edge(Edge edge[], int head[], int u, int v, int w, int& cnt){
  edge[cnt].next = head[u];
  edge[cnt].to = v;
  edge[cnt].val = w;
  head[u] = cnt++;
}
struct st{
  int id;
  int val;
  st(){}
  st(int id, int val): id(id), val(val){}
  bool operator < (const st &B)const{
    return val > B.val;
  }
};
void dijkstra(int s, int dis[], Edge edge[], int head[], bool vis[]){
  dis[s] = 0;
  priority_queue<st> q;
  q.push(st(s, dis[s]));
  while(!q.empty()){
    st u = q.top();
    q.pop();
    if(vis[u.id]) continue;
    vis[u.id] = true;
    for(int i=head[u.id];~i;i=edge[i].next){
      int v = edge[i].to;
      if(!vis[v] && u.val + edge[i].val < dis[v]){
        dis[v] = u.val + edge[i].val;
        q.push(st(v, dis[v]));
      }
    }
  }
}
int main(){
  int t;
  scanf("%d", &t);
  while(t--){
    scanf("%d%d", &n, &m);
    cnt1 = cnt2 = 0;
    memset(head1, -1, sizeof head1);
    memset(head2, -1, sizeof head2);
    memset(dis1, 0x3f, sizeof dis1);
    memset(dis2, 0x3f, sizeof dis2);
    memset(vis1, 0, sizeof vis1);
    memset(vis2, 0, sizeof vis2);
    for(int i=0;i<m;i++){
      int u, v, w;
      scanf("%d%d%d", &u, &v, &w);
      Add_Edge(edge1, head1, u, v, w, cnt1);
      Add_Edge(edge2, head2, v, u, w, cnt2);
    }
    priority_queue<st> q;
    dijkstra(1, dis1, edge1, head1, vis1);
    dijkstra(1, dis2, edge2, head2, vis2);
    ll ans = 0;
    for(int i=1;i<=n;i++){
      ans += dis1[i];
    }
    for(int i=1;i<=n;i++){
      ans += dis2[i];
    }
    printf("%lld\n", ans);
  } 
  return 0;
}

11. poj 3159 Candies

  • u->v边权为w表示u认为v应该得到的值小于或等于w,问第n个点和第1个点的最大差值是多少
  • 如何考虑呢?小于或等于w,那么如果想让差值最大,那么显然应该取w,为了让所有的都恰好满足要求,跑一遍最短路即可,这样得到的就是1和n的最大差值
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 2e5 + 10;
int n, m;
struct Edge{
  int next;
  int to;
  int val;
}edge[N << 1];
int head[N];
int vis[N];
int cnt;
void Add_Edge(int u, int v, int w){
  edge[cnt].next = head[u];
  edge[cnt].to = v;
  edge[cnt].val = w;
  head[u] = cnt++;
}
struct st{
  int id;
  int val;
  st(){}
  st(int id, int val): id(id), val(val){}
  bool operator < (const st &B)const{
    return val > B.val;
  }
};
int dis[N];
void dijkstra(int s){
  memset(dis, 0x3f, sizeof dis);
  dis[s] = 0;
  priority_queue<st> q;
  q.push(st(s, dis[s]));
  while(!q.empty()){
    st u = q.top();
    q.pop();
    if(vis[u.id]) continue;
    vis[u.id] = true;
    for(int i=head[u.id];~i;i=edge[i].next){
      int v = edge[i].to;
      if(!vis[v] && u.val + edge[i].val < dis[v]){
        dis[v] = u.val + edge[i].val;
        q.push(st(v, dis[v]));
      }
    }
  }
}
int main(){
  int n, m;
  scanf("%d%d", &n, &m);
  memset(head, -1, sizeof head);
  for(int i=1;i<=m;i++){
    int u, v, w;
    scanf("%d%d%d", &u, &v, &w);
    Add_Edge(u, v, w);
  }
  dijkstra(1);
  printf("%d", dis[n]);
  return 0;
}

12. poj 2502 Subway

  • 给你一个起点,一个终点,每个地铁线路,在同一线路内部道路双向,步行速度为10km/h,地铁速度为40km/h,问从起点到终点所花费的最短时间
  • 数据比较小,显然可以Floyd,那么有两种移动方式,我们先统计所有点之间步行花费的分钟,然后再统计同一线路内部点之间的花费分钟数,那么最后跑一遍Floyd即可
  • 可以用第201个点表示最后一个点,这样比较方便,四舍五入就+0.5再向下取整即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
const int N = 202;
double edge[N][N];
struct st{
  int id;
  int x;
  int y;
  st(){}
  st(int id, int x, int y): id(id), x(x), y(y){}
};
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int x1, y1, x2, y2;
  cin >> x1 >> y1 >> x2 >> y2;
  int x, y;
  vector<st> vs, v;
  v.push_back(st(0, x1, y1));
  int cnt = 0;
  for(int i=0;i<N;i++){
    for(int j=0;j<N;j++){
      edge[i][j] = 1e18;
    }
  }
  while(cin >> x >> y){
    if(x == -1 && y == -1){
      for(int i=0;i<(int)vs.size();i++){
        edge[0][vs[i].id] = hypot(vs[i].x - x1, vs[i].y - y1) * 3.0 / 500;
        edge[vs[i].id][0] = edge[0][vs[i].id];
        edge[201][vs[i].id] = hypot(vs[i].x - x2, vs[i].y - y2) * 3.0 / 500;
        edge[vs[i].id][201] = edge[201][vs[i].id];
      }
      for(int i=1;i<(int)vs.size();i++){
        edge[vs[i - 1].id][vs[i].id] = hypot(vs[i].x - vs[i - 1].x, vs[i].y - vs[i - 1].y) * 3.0 / 2000;
        edge[vs[i].id][vs[i - 1].id] = edge[vs[i - 1].id][vs[i].id];
      }
      vs.clear();
      continue;
    }
    cnt += 1;
    v.push_back(st(cnt, x, y));
    vs.push_back(st(cnt, x, y));
  }
  v.push_back(st(201, x2, y2));
  for(int i=0;i<(int)v.size();i++){
    for(int j=0;j<(int)v.size();j++){
      edge[v[i].id][v[j].id] = min(edge[v[i].id][v[j].id], hypot(v[i].x - v[j].x, v[i].y - v[j].y) * 3.0 / 500);
      edge[v[j].id][v[i].id] = edge[v[i].id][v[j].id];
    }
  }
  for(int k=0;k<N;k++){
    for(int i=0;i<N;i++){
      for(int j=0;j<N;j++){
        edge[i][j] = min(edge[i][j], edge[i][k] + edge[k][j]);
      }
    }
  }
  cout << (int)(edge[0][N - 1] + 0.5);
  return 0;
}

13 poj 1062 昂贵的聘礼

  • 给你若干组兑换关系,但有限制条件兑换过程中不能出现最大权值差大于 m m m,问最少需要多少钱能换出来1号
  • Floyd记录传递过程中的最大值和最小值,如果发现非法则不能进行传递,同时更新答案,最后计算到达1号节点的最小值即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 200;

struct st{
  int p, l, x;
};
pair<int, int> mp[N][N];

const int INF = 0x3f3f3f3f;
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> m >> n;
  vector<st> s(n + 1);
  vector<vector<pair<int, int> > > vs(n + 1);
  vector<vector<pair<int, int> > > dis(n + 1, vector<pair<int, int> >(n + 1));// [路径上最小的地位等级, 最大的地位等级]
  vector<vector<int> > g(n + 1, vector<int>(n + 1));
  for(int i=1;i<=n;i++){
    for(int j=1;j<=n;j++){
      dis[i][j].first = 0;
      dis[i][j].second = n + 1;
      g[i][j] = INF;
    }
    g[i][i] = 0;
  }
  for(int i=1;i<=n;i++){
    cin >> s[i].p >> s[i].l >> s[i].x;
    dis[i][i].first = dis[i][i].second = s[i].l;
    for(int j=1;j<=s[i].x;j++){
      int u, v;
      cin >> u >> v;
      g[i][u] = v;
      vs[i].push_back(make_pair(u, v));
    }
  }
  for(int i=1;i<=n;i++){
    for(int j=0;j<(int)vs[i].size();j++){
      if(abs(s[i].l - s[vs[i][j].first].l) <= m){
        dis[i][vs[i][j].first].first = min(s[i].l, s[vs[i][j].first].l);
        dis[i][vs[i][j].first].second = max(s[i].l, s[vs[i][j].first].l);
      }else{
        g[i][vs[i][j].first] = INF;
      }
    }
  }
  for(int k=1;k<=n;k++){
    for(int i=1;i<=n;i++){
      for(int j=1;j<=n;j++){
        if(abs(dis[i][k].first - dis[k][j].second) > m || abs(dis[i][k].second - dis[k][j].first) > m) continue;
        dis[i][j].first = min(dis[i][k].first, dis[k][j].first);
        dis[i][j].second = max(dis[i][k].second, dis[k][j].second);
        g[i][j] = min(g[i][k] + g[k][j], g[i][j]);     
      }
    }
  }
  int ans = INF;
  for(int i=1;i<=n;i++){
    ans = min(ans, g[1][i] + s[i].p);
  }
  cout << ans;
  return 0;
}

14 poj 1847 Tram

  • 给你一些路径,默认走第一个,如果走别的就需要加一次操作,问从 a a a b b b需要的最少操作次数
  • 只要能读懂题,就好了

In every intersection there is a switch pointing to the one of the rails going out of the intersection. When the tram enters the intersection it can leave only in the direction the switch is pointing. If the driver wants to go some other way, he/she has to manually change the switch.
每个十字路口都有一个道岔,指向驶出十字路口的一条铁轨。当有轨电车进入十字路口时,它只能沿着道岔指向的方向离开。如果司机想换路,他/她必须手动改变开关。

  • 这可真晦涩
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;

const int INF = 0x3f3f3f3f;
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, a, b;
  cin >> n >> a >> b;
  vector<vector<int> > g(n + 1, vector<int> (n + 1, INF));
  for(int i=1;i<=n;i++){
    int k;
    cin >> k;
    for(int j=0;j<k;j++){
      int v;
      cin >> v;
      if(j == 0) g[i][v] = 0;
      else g[i][v] = 1;
    }
  }
  for(int k=1;k<=n;k++){
    for(int i=1;i<=n;i++){
      for(int j=1;j<=n;j++){
        g[i][j] = min(g[i][k] + g[k][j], g[i][j]);
      }
    }
  }
  cout << (g[a][b] == INF ? -1 : g[a][b]);
  return 0;
}

15 lightoj 1074 Extended Traffic

  • 有向图,可能存在负环,无重边,问从1号节点到其他任意节点能够达到的最小值
  • 如果在走路的途中发现经过了负环,那么经过的所有点势必都会达到无穷小,点数边数比较小,考虑使用spfa判负环,如果找到,那么从出发的点开始进行搜索,这之间所有点都为无穷小
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
#include <iomanip>
#include <queue>
#include <set>
#include <stack>

using namespace std;

const int N = 300;
int a[N];
int dis[N];
int upd[N];
const int INF = 0x3f3f3f3f;
struct Edge{
  int next;
  int to;
  int val;
}edge[N * N];
int head[N];
int cnt;
void Add_Edge(int u, int v, int w){
  edge[cnt].next = head[u];
  edge[cnt].to = v;
  edge[cnt].val = w;
  head[u] = cnt++;
}
int vis[N];
int f[N];
void Dfs(int u){
  for(int i=head[u];~i;i=edge[i].next){
    int v = edge[i].to;
    if(f[v]) continue;
    f[v] = 1;
    Dfs(v);
  }
}
int spfa(int s, int n){
  queue<int> q;
  dis[s] = 0;
  vis[s] = 1;
  q.push(s);
  while(!q.empty()){
    int u = q.front();
    q.pop();
    vis[u] = 0;
    if(f[u]) continue;
    for(int i=head[u];~i;i=edge[i].next){
      int v = edge[i].to;
      if(dis[v] > dis[u] + edge[i].val){
        dis[v] = dis[u] + edge[i].val;
        if(!vis[v]){
          upd[v] += 1;
          if(upd[v] > n){// 更新次数超过n次, 说明出现负环, 那么所有能够达到这个点的点都会无穷小
            f[u] = 1;
            Dfs(u);
          }else{
            q.push(v);
            vis[v] = 1;
          }
        }
      }
    }
  }
}
int cal(int x){
  return x * x * x;
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int t;
  cin >> t;
  for(int kase=1;kase<=t;kase++){
    int n;
    cin >> n;
    memset(head, -1, sizeof head);
    memset(f, 0, sizeof f);
    memset(vis, 0, sizeof vis);
    memset(upd, 0, sizeof upd);
    memset(dis, 0x3f, sizeof dis);
    cnt = 0;
    for(int i=1;i<=n;i++) cin >> a[i];
    int m, q;
    cin >> m;
    for(int i=0;i<m;i++){
      int u, v;
      cin >> u >> v;
      Add_Edge(u, v, cal(a[v] - a[u]));
    }
    spfa(1, n);
    cin >> q;
    cout << "Case " << kase << ":\n";
    while(q--){
      int u;
      cin >> u;
      if(dis[u] == INF || dis[u] < 3 || f[u]){
        cout << "?\n";
      }else{
        cout << dis[u] << '\n';
      }
    }
  }
  return 0;
}

16 hdu 4725 The Shortest Path in Nya Graph

  • 在层内部移动每次需要花费 c c c,在层间移动不需要花费,给定一些边和层的关系,问从1到n的最短路
  • 考虑拆点,设 u u u所在的层为 u + n u+n u+n,建边的时候我们建立一条从 u u u u + n u+n u+n的有向边,其余为无向边
  • 题目描述有问题,应该是m行
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 2e5 + 10;
struct Edge{
  int next;
  int to;
  ll val;
}edge[N << 2];
int cnt = 0;
int head[N];
ll dis[N];
const ll INF = 0x3f3f3f3f3f3f3f3f;
void Add_Edge(int u, int v, int w){
  edge[cnt].next = head[u];
  edge[cnt].to = v;
  edge[cnt].val = w;
  head[u] = cnt++;
}
struct st{
  int id;
  ll val;
  st(){}
  st(int id, ll val): id(id), val(val){}
  bool operator <(const st &B)const{
    return val > B.val;
  }
};
bool vis[N];
ll dijkstra(int s, int n){
  dis[s] = 0;
  priority_queue<st> q;
  q.push(st(s, dis[s]));
  while(!q.empty()){
    auto u = q.top();
    q.pop();
    if(vis[u.id]) continue;
    vis[u.id] = 1;
    for(int i=head[u.id];~i;i=edge[i].next){
      int v = edge[i].to;
      if(!vis[v] && dis[v] > dis[u.id] + edge[i].val){
        dis[v] = dis[u.id] + edge[i].val;
        q.push(st(v, dis[v]));
      }
    }
  }
  return dis[n];
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int t;
  cin >> t;
  for(int kase=1;kase<=t;kase++){
    int n, m;
    ll c;
    cin >> n >> m >> c;
    cnt = 0;
    memset(head, -1, sizeof head);
    memset(vis, 0, sizeof vis);
    memset(dis, 0x3f, sizeof dis);   
    for(int i=1;i<=n;i++){
      int u;
      cin >> u;
      Add_Edge(i, u + n, 0);
      if(u > 1){
        Add_Edge(i, u + n - 1, c);
        Add_Edge(u + n - 1, i, c);
      }
      if(u < n){
        Add_Edge(i, u + n + 1, c);
        Add_Edge(u + n + 1, i, c);
      }
    }
    for(int i=1;i<=m;i++){
      int u, v;
      ll w;
      cin >> u >> v >> w;
      Add_Edge(u, v, w);
      Add_Edge(v, u, w);
    }
    ll ans = dijkstra(1, n);
    if(n == 0) ans = -1;
    if(n == 1) ans = 0;
    cout << "Case #" << kase << ": ";
    cout << (ans == INF ? -1 : ans) << '\n';
  }
  return 0;
}

17 hdu 3416 Marriage Match IV

18 hdu 4370 0 or 1

  • 我们可以发现,这些关系可以概括成几条:1号节点的出度为1,4号节点的入度为1,其余节点的出度和入度相等
  • 能够发现,如果我们求一个从1到n的最短路,即可满足条件,但是还有另一种情况,就是从1到1加上从n到n,也同样满足条件,所以要取一个最小
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 305;
int mp[N][N];
int dis[N];

int vis[N];
const int INF = 0x3f3f3f3f;
void spfa(int s, int n){
  queue<int> q;
  vis[s] = 1;
  for(int i=1;i<=n;i++){
    if(i == s){
      dis[i] = INF;
    }else{
      q.push(i);
      dis[i] = mp[s][i];
      vis[i] = 1;
    }
  }
  while(!q.empty()){
    int u = q.front();
    q.pop();
    vis[u] = 0;
    for(int i=1;i<=n;i++){
      if(dis[i] > dis[u] + mp[u][i]){
        dis[i] = dis[u] + mp[u][i];
        if(!vis[i]){
          vis[i] = 1;
          q.push(i);
        }
      }
    }
  }
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  while(cin >> n){
    for(int i=1;i<=n;i++){
      for(int j=1;j<=n;j++){
        cin >> mp[i][j];
      }
    }
    spfa(1, n);
    int d1 = dis[1];
    int d = dis[n];
    spfa(n, n);
    int d2 = dis[n];
    cout << min(d1 + d2, d) << '\n';
  }
  return 0;
}

19 poj 3169 Layout

  • 差分约束,在这类问题中,会给出若干组 ≤ \leq 或者 ≥ \geq 的关系,比如说 d i s [ u ] ≤ d i s [ v ] + w dis[u]\leq dis[v]+w dis[u]dis[v]+w,那么我连一条边v->u,边权为w,之后跑最短路,就能够求出最大值(考虑把这些不等关系加在一起,最后得到一个小于等于,也就是最大值);反之跑最长路求最小值
  • 这就是最短路问题中的三角不等式,因为我们在更新最短路的时候,一般是如果 d i s [ v ] ≥ d i s [ u ] + w dis[v]\geq dis[u]+w dis[v]dis[u]+w,那么更新 d i s [ v ] dis[v] dis[v],这就是从u->v的边,更新完了以后,有 d i s [ v ] ≤ d i s [ u ] + w dis[v]\leq dis[u]+w dis[v]dis[u]+w,这也就说明了这样连边的道理
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;

const int N = 10005;

struct Edge{
  int next;
  int to;
  int val;
}edge[3 * N];
int cnt = 0;
int head[N];
void Add_Edge(int u, int v, int w){
  edge[cnt].next = head[u];
  edge[cnt].to = v;
  edge[cnt].val = w;
  head[u] = cnt++;
}
int vis[N];
int dis[N];
int num[N];
const int INF = 0x3f3f3f3f;
int spfa(int s, int n){
  queue<int> q;
  q.push(s);
  vis[s] = 1;
  num[s] = 1;
  memset(dis, 0x3f, sizeof dis);
  dis[s] = 0;
  while(!q.empty()){
    int u = q.front();
    q.pop();
    vis[u] = 0;
    for(int i=head[u];~i;i=edge[i].next){
      int v = edge[i].to;
      if(dis[v] > dis[u] + edge[i].val){
        dis[v] = dis[u] + edge[i].val;
        num[v] += 1;
        if(num[v] >= n){
          return -1;
        }
        if(!vis[v]){
          q.push(v);
          vis[v] = 1;
        }
      }
    }
  }
  if(dis[n] == INF) return -2;
  return dis[n];
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, ML, MD;
  cin >> n >> ML >> MD;
  memset(head, -1, sizeof head);
  for(int i=0;i<ML;i++){// 统一小的指向大的
    int u, v, w;
    cin >> u >> v >> w;
    if(u > v) swap(u, v);// dis[v] - dis[u] <= w <=> dis[v] <= dis[u] + w <=> [u, v] = w
    Add_Edge(u, v, w);
  }
  for(int i=0;i<MD;i++){
    int u, v, w;
    cin >> u >> v >> w;
    if(u > v) swap(u, v);// dis[v] - dis[u] >= w <=> dis[u] <= dis[v] - w <=> [v, u] = -w
    Add_Edge(v, u, -w);
  }
  for(int i=1;i<n;i++){
    Add_Edge(i + 1, i, 0); // dis[i] <= dis[i + 1]
  }
  cout << spfa(1, n) << '\n';
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值