大白叔叔专题之最短路、最小生成树(二)

1、uva 11374 Airport Express

  题意:给出若干经济舱的路线和商务舱的路线,但只能选择乘坐一次商务舱。求前往机场的最短时间。

  思路:分别从起点和终点求出到其他点的最短路,枚举每个商务舱,记录最小值。

  1 #include<iostream>
  2 #include<vector>
  3 #include<queue>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<cstdio>
  7 using namespace std;
  8 const int maxn = 510;
  9 const int INF = 0x3f3f3f3f;
 10 struct edge
 11 {
 12     int from, to, cost,next;
 13     edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cost(cc),next(nn){ }
 14 };
 15 vector<edge>Edge;
 16 int Head[maxn], totedge;
 17 int N, S, E;
 18 int M, K;
 19 struct DIJ
 20 {
 21     bool vis[maxn];
 22     int preS[maxn];
 23     int disS[maxn];
 24     int disT[maxn];
 25     int preT[maxn];
 26     int st, ed,n;
 27     void set(int nodes,int source,int dest)
 28     {
 29         n=nodes,st = source, ed = dest;
 30     }
 31     void Init()
 32     {
 33         memset(Head, -1, sizeof(Head));
 34         totedge = 0;
 35         Edge.clear();
 36     }
 37     void addedge(int from, int to, int cost)
 38     {
 39         Edge.push_back(edge(from, to, cost, Head[from]));
 40         Head[from] = totedge++;
 41         Edge.push_back(edge(to, from, cost, Head[to]));
 42         Head[to] = totedge++;
 43     }
 44     void cal_dij(int *dis,int *pre)
 45     {
 46         memset(vis, 0, sizeof(vis));
 47         for (int i = 1; i <= n; i++) dis[i] = INF,pre[i]=-1;
 48         for (int i = Head[st]; i != -1; i = Edge[i].next)
 49         {
 50             dis[Edge[i].to] = Edge[i].cost;
 51             pre[Edge[i].to] = st;
 52         }
 53         vis[st] = true, dis[st] = 0;
 54         for (int i = 1; i < n; i++)
 55         {
 56             int u = st, Min = INF;
 57             for (int j = 1; j <= n; j++)
 58             {
 59                 if (!vis[j] && dis[j] < Min) u = j, Min = dis[j];
 60             }
 61             vis[u] = true;
 62             for (int k = Head[u]; k != -1; k = Edge[k].next)
 63             {
 64                 int v = Edge[k].to;
 65                 if (!vis[v] && dis[v] > dis[u] + Edge[k].cost)
 66                 {
 67                     dis[v] = dis[u] + Edge[k].cost;
 68                     pre[v] = u;
 69                 }
 70             }
 71         }
 72     }
 73     void PrintS(int u)
 74     {
 75         if (u != S) PrintS(preS[u]);
 76         if (u == S) printf("%d",u);
 77         else printf(" %d", u);
 78     }
 79     void PrintT(int u)
 80     {
 81         printf(" %d", u);
 82         if(u!=E) PrintT(preT[u]);
 83     }
 84 }dij;
 85 
 86 int main()
 87 {
 88     int Case = 1;
 89     while (~scanf("%d%d%d", &N, &S, &E))
 90     {
 91         if (Case > 1) printf("\n");
 92         scanf("%d", &M);
 93         dij.Init();
 94         for (int i = 1; i <= M; i++)
 95         {
 96             int u, v, c;
 97             scanf("%d%d%d", &u, &v, &c);
 98             dij.addedge(u, v, c);
 99         }
100         dij.set(N, S, E);
101         dij.cal_dij(dij.disS,dij.preS);
102         dij.set(N, E, S);
103         dij.cal_dij(dij.disT,dij.preT);
104         scanf("%d", &K);
105         int ans = dij.disS[E],from,to;
106         for(int i=1;i<=K;i++)
107         {
108             int u, v, c;
109             scanf("%d%d%d", &u, &v, &c);
110             if (dij.disS[u] + dij.disT[v] + c < ans) ans = dij.disS[u] + dij.disT[v] + c, from = u, to = v;
111             if (dij.disS[v] + dij.disT[u] + c < ans) ans = dij.disS[v] + dij.disT[u] + c, from = v, to = u;
112         }
113         if (ans == dij.disS[E])
114         {
115             dij.PrintS(E);
116             printf("\n");
117             printf("Ticket Not Used\n");
118             printf("%d\n", ans);
119         }
120         else
121         {
122             dij.PrintS(from);
123             dij.PrintT(to);
124             printf("\n");
125             printf("%d\n", from);
126             printf("%d\n", ans);
127         }
128         Case++;
129     }
130     return 0;
131 }
View Code

 2、uva 10917 Walk Through the Forest

  题意:办公室在1,家在2.现在需要从办公室回到家,途中会穿过森林中的n-2个结点。给出无向图,如果当前位置再结点u,存在从u-->v的边并且v到家的最短距离小于u到家的距离,则可以从u走到v.现在求所有的方案数。

  思路:先求出所有的点到家的最短距离。然后用dfs得到方案数(类似深搜一棵有根外向树)。

  1 #include<iostream>
  2 #include<queue>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<vector>
  6 using namespace std;
  7 const int maxn = 1100;
  8 const int INF = 0x3f3f3f3f;
  9 struct edge
 10 {
 11     int from, to, cap,next;
 12     edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cap(cc),next(nn){ }
 13 };
 14 vector<edge>Edge;
 15 int Head[maxn], totedge;
 16 struct SPFA
 17 {
 18     int n, st, ed;
 19     int vis[maxn];
 20     int dis[maxn];
 21     int cnt[maxn];
 22     int pre[maxn];
 23     void Init()
 24     {
 25         memset(Head, -1, sizeof(Head));
 26         totedge = 0;
 27         Edge.clear();
 28     }
 29     void set(int nodes, int source, int dest)
 30     {
 31         n = nodes, st = source, ed = dest;
 32     }
 33     void addedge(int from, int to, int cap)
 34     {
 35         Edge.push_back(edge(from, to, cap, Head[from]));
 36         Head[from] = totedge++;
 37         Edge.push_back(edge(to, from, cap, Head[to]));
 38         Head[to] = totedge++;
 39     }
 40     bool cal_spfa()
 41     {
 42         memset(vis, 0, sizeof(vis));
 43         memset(dis, INF, sizeof(dis));
 44         memset(cnt, 0,sizeof(cnt));
 45         memset(pre, -1, sizeof(pre));
 46 
 47         vis[st] = true, cnt[st]++, dis[st] = 0;
 48         queue<int>q;
 49         q.push(st);
 50         while (!q.empty())
 51         {
 52             int u = q.front();
 53             q.pop();
 54             vis[u] = false;
 55 
 56             for (int i = Head[u]; i != -1; i = Edge[i].next)
 57             {
 58                 int v = Edge[i].to;
 59                 if (dis[v] > dis[u] + Edge[i].cap)
 60                 {
 61                     dis[v] = dis[u] + Edge[i].cap;
 62                     pre[v] = u;
 63                     if (!vis[v])
 64                     {
 65                         vis[v] = true;
 66                         q.push(v);
 67                         cnt[v]++;
 68                         if (v > n)return false;
 69                     }
 70                 }
 71             }
 72         }
 73         return true;
 74     }
 75 }spfa;
 76 int n,m;
 77 int Count[maxn];
 78 int dfs(int u)
 79 {
 80     if (Count[u] != 0) return Count[u];
 81     for (int i = Head[u]; i != -1; i = Edge[i].next)
 82     {
 83         int v = Edge[i].to;
 84         if (spfa.dis[v]<spfa.dis[u])
 85         {
 86             int tmp = dfs(v);
 87             Count[u] += tmp;
 88         }
 89     }
 90     return Count[u];
 91 }
 92 int main()
 93 {
 94     while (~scanf("%d", &n) && n)
 95     {
 96         int from, to, d;
 97         spfa.Init();
 98         scanf("%d", &m);
 99         for (int i = 1; i <= m; i++)
100         {
101             scanf("%d%d%d", &from, &to, &d);
102             spfa.addedge(from, to, d);
103         }
104         spfa.set(n, 2, 1);
105         spfa.cal_spfa();
106         memset(Count, 0, sizeof(Count));
107         Count[2] = 1;
108         int ans = dfs(1);
109         printf("%d\n", ans);
110     }
111     return 0;
112 }
View Code

3、uva 10537 The Toll! Revisited

  题意:现在要求运到目的地时要有若干货物。从一个地方离开不需要缴纳费用,但进入一个地方时,需要缴纳费用,如果是village(小写字母表示),只需缴纳一个货物作为费用;如果是town(大写字母表示),每20个货物需要缴纳1个货物作为费用。问出发时最少需要的货物,并给出字典序最小的路径。

  思路:因为边的费用和带的货物的数目有关,逆向求解,如果逆向从u到若干其他结点,需要考虑u是viliage还是town,前者为dis[u]+1,后者为ceil(dis[u]*20.0/19.0).然后记录路径前驱,方便输出。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<queue>
  4 #include<vector>
  5 #include<cstring>
  6 #include<cctype>
  7 #include<cmath>
  8 using namespace std;
  9 const int maxn = 110;
 10 const long long INF = (long long)1<<61;
 11 struct edge
 12 {
 13     int from, to, next;
 14     edge(int ff=0,int tt=0,int nn=0):from(ff),to(tt),next(nn){ }
 15 };
 16 vector<edge>Edge;
 17 int Head[maxn], totedge;
 18 
 19 struct SPFA
 20 {
 21     int n, st, ed;
 22     long long dis[maxn];
 23     int cnt[maxn];
 24     int vis[maxn];
 25     int pre[maxn];
 26 
 27     void Init()
 28     {
 29         memset(Head, -1, sizeof(Head));
 30         totedge = 0;
 31         Edge.clear();
 32     }
 33     void set(int nodes, int source, int dest)
 34     {
 35         n = nodes, st = source, ed = dest;
 36     }
 37     void addedge(int from, int to)
 38     {
 39         Edge.push_back(edge(from, to, Head[from]));
 40         Head[from] = totedge++;
 41         Edge.push_back(edge(to, from, Head[to]));
 42         Head[to] = totedge++;
 43     }
 44     bool cal_spfa(int desnum)
 45     {
 46         memset(vis, 0, sizeof(vis));
 47         memset(dis, 0x3f, sizeof(dis));
 48         memset(pre, -1, sizeof(pre));
 49         memset(cnt, 0, sizeof(cnt));
 50         queue<int>q;
 51         q.push(ed);
 52         vis[ed] = true, dis[ed] = desnum;
 53         while (!q.empty())
 54         {
 55             int u = q.front();
 56             q.pop();
 57             vis[u] = false;
 58             long long  c;
 59             if (u >= 26)
 60             {
 61                 c = (long long)ceil(dis[u]*1.0/19.0);
 62                 //c = (long long)ceil(double(dis[u])*20.0 / 19.0) - dis[u];
 63             }
 64             else c = 1;
 65             for (int i = Head[u]; i != -1; i = Edge[i].next)
 66             {
 67                 int v = Edge[i].to;
 68                 if (dis[v] > dis[u] + c)
 69                 {
 70                     dis[v] = dis[u] + c;
 71                     pre[v] = u;
 72                     if (!vis[v])
 73                     {
 74                         q.push(v);
 75                         vis[v] = true;
 76                         cnt[v]++;
 77                     }
 78                 }
 79                 else if (dis[v] == dis[u] + c)
 80                 {
 81                     if (u > pre[v]&&u>=26&&pre[v]<26) pre[v] = u;
 82                     else if (u < pre[v]&&pre[v]<26) pre[v] = u;
 83                     else if(u<pre[v]&&u>=26)pre[v] = u;
 84                 }
 85             }
 86         }
 87         return true;
 88     }
 89 }spfa;
 90 int n, desnum, st, ed;
 91 int main()
 92 {
 93     char u[4],v[4];
 94     int Case = 1;
 95     while (~scanf("%d", &n) && n != -1)
 96     {
 97         spfa.Init();
 98         for (int i = 1; i <= n; i++)
 99         {
100             scanf("%s%s", u, v);
101             int ui, vi;
102             if (isupper(u[0])) ui = u[0] - 'A' + 26;
103             else ui = u[0] - 'a';
104             if (isupper(v[0])) vi = v[0] - 'A' + 26;
105             else vi = v[0] - 'a';
106             spfa.addedge(ui, vi);
107         }
108         scanf("%d%s%s", &desnum, u, v);
109         
110         if (isupper(u[0])) st = u[0] - 'A' + 26;
111         else st = u[0] - 'a';
112         if (isupper(v[0])) ed = v[0] - 'A' + 26;
113         else ed = v[0] - 'a';
114 
115         spfa.set(60, st, ed);
116         spfa.cal_spfa(desnum);
117         printf("Case %d:\n", Case++);
118         printf("%lld\n", spfa.dis[st]);
119         int tmp =st;
120         while (true)
121         {
122             if (tmp == ed)
123             {
124                 printf("%c\n", v[0]);
125                 break;
126             }
127             char p;
128             if (tmp < 26) p = 'a' + tmp;
129             else p = 'A' + (tmp - 26);
130             printf("%c-", p);
131             tmp = spfa.pre[tmp];
132         }
133     }
134     return 0;
135 }
View Code

4、uva 11090 Going in Cycle!!

  题意:给出有环有向图,求所存在的环中边权平均值最小是多少?

  思路:二分答案,如果所有边都减去当前值,SPFA判断存在负环,则说明当前值太大,最小平均值肯定比它要小;如果不存在负环,则说明减的不够大,最小平均值一定比其大。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<queue>
  4 #include<cstring>
  5 #include<vector>
  6 using namespace std;
  7 const int maxn = 60;
  8 const int INF = 0x3f3f3f3f;
  9 struct edge
 10 {
 11     int from, to, next;
 12     double cost;
 13     edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cost(cc),next(nn){ }
 14 };
 15 vector<edge>Edge;
 16 int Head[maxn], totedge;
 17 int N, M;
 18 
 19 
 20 struct SPFA
 21 {
 22     int n, st;
 23     bool vis[maxn];
 24     double dis[maxn];
 25     int cnt[maxn];
 26     bool visn[maxn];
 27     void Init()
 28     {
 29         memset(Head, -1, sizeof(Head));
 30         totedge = 0;
 31         Edge.clear();
 32     }
 33     void set(int nodes, int source)
 34     {
 35         n = nodes, st = source;
 36     }
 37     void addedge(int from, int to, double cap)
 38     {
 39         Edge.push_back(edge(from, to, cap, Head[from]));
 40         Head[from] = totedge++;
 41     }
 42     bool cal_spfa()
 43     {
 44         memset(vis, 0, sizeof(vis));
 45         memset(dis, INF, sizeof(dis));
 46         memset(cnt, 0, sizeof(cnt));
 47 
 48         queue<int>q;
 49         q.push(st);
 50         vis[st] = true;
 51         cnt[st]++;
 52         dis[st] = 0;
 53         while (!q.empty())
 54         {
 55             int u = q.front();
 56             q.pop();
 57             visn[u] = true;
 58             vis[u] = false;
 59             for (int i = Head[u]; i != -1; i = Edge[i].next)
 60             {
 61                 int v = Edge[i].to;
 62                 if (dis[v] > dis[u] + Edge[i].cost)
 63                 {
 64                     dis[v] = dis[u] + Edge[i].cost;
 65                     if (!vis[v])
 66                     {
 67                         vis[v] = true;
 68                         q.push(v);
 69                         cnt[v]++;
 70                         if (cnt[v] > n) return false;
 71                     }
 72                 }
 73             }
 74         }
 75         return true;
 76     }
 77     bool binsolve(double mid)
 78     {
 79         for (int i = 0; i < totedge; i++) Edge[i].cost -= mid;
 80         memset(visn, 0, sizeof(visn));
 81         bool isok = true;
 82         for (int i = 1; i <= N; i++)
 83         {
 84             if (!visn[i])
 85             {
 86                 set(N, i);
 87                 isok = cal_spfa();
 88                 if (!isok) break;
 89             }
 90         }
 91         for (int i = 0; i < totedge; i++) Edge[i].cost += mid;
 92         return isok;
 93     }
 94 }spfa;
 95 
 96 int main()
 97 {
 98     int t;
 99     scanf("%d", &t);
100     int Case = 1;
101     while (t--)
102     {
103         double L=INF, R=0;
104         scanf("%d%d", &N, &M);
105         spfa.Init();
106         for (int i = 1; i <= M; i++)
107         {
108             int from, to;
109             double cost;
110             scanf("%d%d%lf", &from, &to, &cost);
111             spfa.addedge(from, to, cost);
112             L = min(L, cost);
113             R = max(R, cost);
114         }
115         if (spfa.binsolve(R+1000))
116         {
117             printf("Case #%d: No cycle found.\n", Case++);
118             continue;
119         }
120         while (R - L > 1e-6)
121         {
122             double mid = (R + L) / 2;
123             if (!spfa.binsolve(mid)) R = mid;
124             else L = mid;
125         }
126         printf("Case #%d: %.2lf\n", Case++, L);
127     }
128     return 0;
129 }
View Code

5、uva 11478 Halum

  题意:给出有向图,每一次可以把连向u的所有的边的边权减去di,把从u出发连向其他点的边的边权加上di.问经过若干次操作后,能否使图中所有边的边权都大于0,如果可以给出其最小值。

  思路:对于某一条有向边u--->v,设其边权为w,作用在该边上的最终结果为w+sum(u)-sum(v)>=ans.即sum(v)-sum(u)<=w-ans.即转换为差分约束问题。于是我们二分答案,让所有的边减去ans,然后判断是否存在负环,没有的话说明ans还可以增大,否则只能减小。注意这里的边权和di都是整数。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<queue>
 4 #include<vector>
 5 #include<cstring>
 6 using namespace std;
 7 const int maxn = 510;
 8 const int INF = 0x3f3f3f3f;
 9 struct edge
10 {
11     int from, to, cost, next;
12     edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cost(cc),next(nn){ }
13 };
14 vector<edge>Edge;
15 int Head[maxn], totedge;
16 struct SPFA
17 {
18     int n, st, ed;
19     int cnt[maxn];
20     bool vis[maxn];
21     int dis[maxn];
22 
23     void Init()
24     {
25         memset(Head, -1, sizeof(Head));
26         totedge = 0;
27         Edge.clear();
28     }
29     void addedge(int from, int to, int cost)
30     {
31         Edge.push_back(edge(from, to, cost, Head[from]));
32         Head[from] = totedge++;
33     }
34     bool cal_spfa(int mid)
35     {
36         memset(cnt, 0, sizeof(cnt));
37         queue<int>q;
38         for (int i = 1; i <= n; i++) q.push(i), dis[i] = 0, vis[i] = true,cnt[i]=1;
39         while (!q.empty())
40         {
41             int u = q.front();
42             q.pop();
43             vis[u] = false;
44             for (int i = Head[u]; i != -1; i = Edge[i].next)
45             {
46                 int v = Edge[i].to;
47                 if (dis[v] > dis[u] + Edge[i].cost - mid)
48                 {
49                     dis[v] = dis[u] + Edge[i].cost - mid;
50                     if (!vis[v])
51                     {
52                         vis[v] = true;
53                         q.push(v);
54                         cnt[v]++;
55                         if (cnt[v] >= n) return false;
56                     }
57                 }
58             }
59         }
60         return true;
61     }
62 }spfa;
63 int main()
64 {
65     int N,E;
66     while (~scanf("%d%d", &N, &E))
67     {
68         spfa.Init();
69         int l=1, r = -1;
70         for (int i = 1; i <= E; i++)
71         {
72             int from, to, cost;
73             scanf("%d%d%d", &from, &to, &cost);
74             spfa.addedge(from, to, cost);
75             r = max(r, cost);
76         }
77         spfa.n = N;
78         if (spfa.cal_spfa(r))
79         {
80             printf("Infinite\n");
81         }
82         else if (!spfa.cal_spfa(1))
83         {
84             printf("No Solution\n");
85         }
86         else
87         {
88             while (l <= r)
89             {
90                 int mid = (l + r) / 2;
91                 if (spfa.cal_spfa(mid)) l = mid+1;
92                 else r = mid - 1;
93             }
94             printf("%d\n", r);
95         }
96     }
97     return 0;
98 }
View Code

 6、uvalive 3661 Animal Run

  题意:有n*m的方格,有横、竖、主对角线边。一群动物想要从左上角到达右下角。为了防止逃跑,每条道路都需要若干人员堵住。现在需要求最少的人数,使得没有一只动物能够逃离。

  思路:最简单的思路:求原图的最小割。但是会超时。因为原图为S-T平面图,转换为其对偶图,求由原图的面所构成的S*到T*的最短路即原图S到T的最小割。用SPFA的话,需要用优先队列优化。

  1 #include<iostream>
  2 #include<cstring>
  3 #include<queue>
  4 #include<cstdio>
  5 using namespace std;
  6 const int maxe = 1000 * 1000 * 3 * 2 + 100;
  7 const int maxn = 1000 * 1000*2 + 100;
  8 const int INF = 0x3f3f3f3f;
  9 struct edge
 10 {
 11     int from, to, cost, next;
 12     edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cost(cc),next(nn){ }
 13 }Edge[maxe];
 14 int Head[maxn],totedge;
 15 
 16 struct node
 17 {
 18     int id, dist;
 19     node(int ii=0,int dd=0):id(ii),dist(dd){ }
 20     friend bool operator<(const node&a, const node&b)
 21     {
 22         return a.dist > b.dist;
 23     }
 24 };
 25 struct SPFA
 26 {
 27     int st, ed;
 28     int dis[maxn];
 29     bool vis[maxn];
 30     void Init()
 31     {
 32         memset(Head, -1, sizeof(Head));
 33         totedge = 0;
 34     }
 35     void Set(int source, int dest)
 36     {
 37         st = source, ed = dest;
 38     }
 39     void addedge(int from, int to, int cost)
 40     {
 41         Edge[totedge] = edge(from, to, cost, Head[from]);
 42         Head[from] = totedge++;
 43         Edge[totedge] = edge(to, from, cost, Head[to]);
 44         Head[to] = totedge++;
 45     }
 46     void Cal_spfa()
 47     {
 48         memset(dis, INF, sizeof(dis));
 49         memset(vis, 0, sizeof(vis));
 50         dis[st] = 0;
 51         priority_queue<node>q;
 52         q.push(node(st,0));
 53         while (!q.empty())
 54         {
 55             node cur = q.top();
 56             q.pop();
 57             int u = cur.id;
 58             if (vis[u]) continue;
 59             vis[u] = true;
 60             for (int i = Head[u]; i != -1; i = Edge[i].next)
 61             {
 62                 int v = Edge[i].to;
 63                 if (dis[v] > dis[u] + Edge[i].cost)
 64                 {
 65                     dis[v] = dis[u] + Edge[i].cost;
 66                     q.push(node(v, dis[v]));
 67                 }
 68             }
 69         }
 70     }
 71 }spfa;
 72 int main()
 73 {
 74     int n, m;
 75     int Case = 1;
 76     while (~scanf("%d%d", &n, &m) && (n + m))
 77     {
 78         spfa.Init();
 79         int nums = (n-1)*(m-1) * 2;
 80         int source = nums + 1, dest = nums + 2;
 81         //
 82         for (int i = 1; i <= n; i++)
 83         {
 84             for (int j = 1; j <= m - 1; j++)
 85             {
 86                 int cap;
 87                 scanf("%d", &cap);
 88                 if (i == 1)
 89                 {
 90                     spfa.addedge(j * 2, dest, cap);
 91                 }
 92                 else if (i == n) spfa.addedge(source, (i - 2) *(m-1)* 2 + j * 2-1, cap);
 93                 else
 94                 {
 95                     spfa.addedge((i - 2)*(m - 1) * 2 + j * 2 - 1, (i - 1)*(m - 1) * 2 + j * 2, cap);
 96                 }
 97             }
 98         }
 99         //
100         for (int i = 1; i <= n - 1; i++)
101         {
102             for (int j = 1; j <= m; j++)
103             {
104                 int cap;
105                 scanf("%d", &cap);
106                 if (j == 1)
107                 {
108                     spfa.addedge(source, (i - 1)*(m - 1)*2 + 1, cap);
109                 }
110                 else if (j == m)
111                 {
112                     spfa.addedge(i*(m-1)*2, dest, cap);
113                 }
114                 else
115                 {
116                     spfa.addedge((i - 1)*(m - 1) * 2 + (j - 1) * 2, (i - 1)*(m - 1) * 2 + (j - 1) * 2 + 1, cap);
117                 }
118             }
119         }
120         //对角线
121         for (int i = 1; i <= n - 1; i++)
122         {
123             for (int j = 1; j <= m - 1; j++)
124             {
125                 int cap;
126                 scanf("%d", &cap);
127                 spfa.addedge((i - 1)*(m - 1) * 2 + j * 2 - 1,(i - 1)*(m - 1) * 2 + j * 2, cap);
128             }
129         }
130         spfa.Set(source, dest);
131         spfa.Cal_spfa();
132         printf("Case %d: Minimum = %d\n", Case++, spfa.dis[dest]);
133     }
134     return 0;
135 }
View Code

 7、uva 10603 Fill

  题意:有三个水壶a,b,c,其中a和b是空的,c是满的。每次可以把水从一个壶倒到另一个壶,直到前一个已经倒空或者后一个壶已经倒满。问最少的累计倒水的体积,使得其中某一个壶的水的体积为d?如果达不到d,则输出离d最近的那个目标体积的对应最少的累计倒水的体积。

  思路:通过BFS搜索,记录已经访问过的状态,每次更新达到壶中水的体积最少的累计次数。

 1 #include<iostream>
 2 #include<queue>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cstdio>
 6 using namespace std;
 7 int c[3], d;
 8 const int maxv = 210;
 9 int dist[maxv];//得到容量为i最少的倒水体积
10 bool vis[maxv][maxv];//表示a、b、c状态为i、j、d-i-j是否访问过
11 struct node
12 {
13     int dis;//从开始到当前已倒过的水的体积
14     int v[3];
15     node(int dd=0,int vva=0,int vvb=0,int vvc=0):dis(dd)
16     {
17         v[0] = vva, v[1] = vvb, v[2] = vvc;
18     }
19     friend bool operator<(const node&a, const node&b)
20     {
21         return a.dis > b.dis;
22     }
23 };
24 void BFS()
25 {
26     memset(dist, -1, sizeof(dist));
27     memset(vis, 0, sizeof(vis));
28     priority_queue<node>q;
29     q.push(node(0, 0, 0, c[2]));
30     vis[0][0] = true;
31     while (!q.empty())
32     {
33         node cur = q.top();
34         q.pop();
35         for (int i = 0; i < 3; i++)
36         {
37             if (dist[cur.v[i]] == -1 || dist[cur.v[i]] > cur.dis) dist[cur.v[i]] = cur.dis;
38         }
39         for (int i = 0; i < 3; i++)
40         {
41             for (int j = 0; j < 3; j++)
42             {
43                 if (i == j || cur.v[i] == 0 || cur.v[j] == c[j]) continue;
44                 int add = min(cur.v[i], c[j] - cur.v[j]);
45                 node tmp = cur;
46                 tmp.v[i] -= add;
47                 tmp.v[j] += add;
48                 tmp.dis += add;
49                 if (!vis[tmp.v[0]][tmp.v[1]])//由于肯定会越倒,dis肯定越大,所以到后面如果出现重复的,则不再添加
50                 {
51                     vis[tmp.v[0]][tmp.v[1]] = 1;
52                     q.push(tmp);
53                 }
54             }
55         }
56     }
57 }
58 int main()
59 {
60     int t;
61     scanf("%d", &t);
62     while (t--)
63     {
64         for (int i = 0; i <= 2; i++) scanf("%d", &c[i]);
65         scanf("%d", &d);
66         BFS();
67         while (d >= 0)
68         {
69             if (dist[d] >= 0)
70             {
71                 printf("%d %d\n", dist[d], d);
72                 break;
73             }
74             d--;
75         }
76     }
77     return 0;
78 }
View Code

 8、uva 10269 Adventure of Super Mario

  题意:给出无向图,求起点到终点的最小花费。每条路,如果走路,花费时间等于路的长度;如果坐magic boot,可以直达,时间为0.一次坐magic boot,可以经过多条道路,但是所有经过道路总长不超过L,并且途中不能穿过城堡。magic boot最多只能使用K次。

  思路:先求出两两可直达的最短距离(即途中不经过城堡)。然后spfa判断dis[i][k](到i处剩余k次时的最小花费)之间的转换,可通过优先队列优化。(原本以为dis一维可以,用队列记录到达的结点和飞行情况,但是解不出来……)

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <cmath>
  5 #include <cstdlib>
  6 #include <queue>
  7 using namespace std;
  8 
  9 const int maxn = 155;
 10 const int INF = 0x3f3f3f3f;
 11 typedef long long ll;
 12 int A, B, M, L, K;
 13 int mp[maxn][maxn];
 14 
 15 void floyd()
 16 {
 17     for (int k = 1; k <= A; k++)
 18     {
 19         for (int i = 1; i <= A + B; i++)
 20         {
 21             for (int j = 1; j <= A + B; j++)
 22             {
 23                 if (mp[i][j] > mp[i][k] + mp[k][j])
 24                 {
 25                     mp[i][j] = mp[i][k] + mp[k][j];
 26                 }
 27             }
 28         }
 29     }
 30 }
 31 
 32 struct node
 33 {
 34     int u, k, cos;//当前结点、剩余次数、花费
 35     node(int uu = 0, int kk = 0, int cc = 0) :u(uu), k(kk), cos(cc)
 36     {}
 37     bool operator <(const node& a)const
 38     {
 39         return cos > a.cos;
 40     }
 41 };
 42 int dis[maxn][15];
 43 int spfa()
 44 {
 45     priority_queue<node> Q;
 46     for (int i = 0; i <= A + B; i++)
 47     {
 48         for (int j = 0; j <= 12; j++)
 49         {
 50             dis[i][j] = INF;
 51         }
 52     }
 53     Q.push(node(A + B, K, 0));
 54     dis[A + B][K] = 0;
 55     while (!Q.empty())
 56     {
 57         int u = Q.top().u;
 58         int k = Q.top().k;
 59         if (u == 1) return Q.top().cos;
 60         Q.pop();
 61         for (int i = 1; i <= A + B; i++)
 62         {
 63             if (i == u) continue;
 64             if (mp[u][i] == INF) continue;
 65             if (dis[i][k] > dis[u][k] + mp[u][i])
 66             {
 67                 dis[i][k] = dis[u][k] + mp[u][i];
 68                 Q.push(node(i, k, dis[i][k]));
 69             }
 70             if (mp[u][i] <= L && dis[i][k - 1] > dis[u][k] && k != 0)
 71             {//如果可以花费一次使用次数从u直接到i
 72                 dis[i][k - 1] = dis[u][k];
 73                 Q.push(node(i, k - 1, dis[i][k - 1]));
 74             }
 75         }
 76     }
 77 }
 78 
 79 int main()
 80 {
 81     int T;
 82     scanf("%d", &T);
 83     while (T--)
 84     {
 85         scanf("%d %d %d %d %d", &A, &B, &M, &L, &K);
 86         memset(mp, INF, sizeof(mp));
 87         for (int i = 1; i <= A + B; i++)
 88         {
 89             mp[i][i] = 0;
 90         }
 91         int a, b, c;
 92         for (int i = 0; i < M; i++)
 93         {
 94             scanf("%d %d %d", &a, &b, &c);
 95             mp[a][b] = mp[b][a] = c;
 96         }
 97         floyd();
 98         printf("%d\n", spfa());
 99     }
100     return 0;
101 }
View Code

 

 

posted @ 2017-11-17 06:02  萌萌的美男子  阅读(287)  评论(0编辑  收藏  举报