最小生成树和最短路
- 题目列表
- A Jungle Roads(POJ1251)
- B Networking(POJ1287)
- C Building a Space Station(POJ2031)
- D Constructing Roads(POJ2421)
- E Truck History(POJ1789)
- F Arctic Network(POJ2349)
- G Highways(POJ1751)
- H Til the Cows Come Home(POJ2387)
- I Frogger(POJ2253)
- J Heavy Transportation(POJ1797)
- K Silver Cow Party(POJ3268)
- L Currency Exchange(POJ1860)
- M Wormholes(POJ3259)
- N MPI Maelstrom(POJ1502)
- O Cow Contest(POJ3660)
- P Arbitrage (POJ2240)
- 总结
题目列表
A Jungle Roads(POJ1251)
最小生成树板子题,直接套板子就能过。
AC代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int max_n = 1e5;
int parent[max_n];
int myRank[max_n];
void init(int n)
{
for (int i = 0; i < n; i++)
{
parent[i] = i;
myRank[i] = 0;
}
}
int find(int x)
{
if (x != parent[x])
parent[x] = find(parent[x]);
return parent[x];
}
void unite(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
return;
if (myRank[x] < myRank[y])
{
parent[x] = y;
}
else
{
parent[y] = x;
if (myRank[x] == myRank[y])
{
myRank[x]++;
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
struct edge
{
int stN, edN, cost;
bool operator<(edge e1)
{
return this->cost < e1.cost;
}
} edges[max_n];
int krukal(int v, int e)
{
sort(edges, edges + e);
init(v);
int res = 0;
int nedge = 0;
for (int i = 0; i < e && nedge != v - 1; i++)
{
edge et = edges[i];
if (!same(et.stN, et.edN))
{
unite(et.stN, et.edN);
res += et.cost;
nedge++;
}
}
if (nedge < v - 1)
res = -1;
return res;
}
int main()
{
int n, index;
while (cin >> n)
{
if (n == 0)
break;
cin >> index;
for (int i = 0; i < index; i++)
cin >> edges[i].stN >> edges[i].edN >> edges[i].cost;
int ans = krukal(n, index);
cout << ans << endl;
}
return 0;
}
B Networking(POJ1287)
还是板子题,没有什么需要注意的地方。
AC代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int max_n = 1e5;
int parent[max_n];
void init(int n)
{
for (int i = 0; i < n + 10; i++)
parent[i] = i;
}
int find(int x)
{
if (x != parent[x])
parent[x] = find(parent[x]);
return parent[x];
}
void unite(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
return;
parent[x] = y;
}
bool same(int x, int y)
{
return find(x) == find(y);
}
struct edge
{
int stN, edN, cost;
bool operator<(edge e1)
{
return this->cost < e1.cost;
}
} edges[max_n];
int krukal(int v, int e)
{
sort(edges, edges + e);
init(v);
int res = 0;
int nedge = 0;
for (int i = 0; i < e && nedge != v - 1; i++)
{
edge et = edges[i];
if (!same(et.stN, et.edN))
{
unite(et.stN, et.edN);
res += et.cost;
nedge++;
}
}
if (nedge < v - 1) //不连通
res = -1;
return res;
}
int main()
{
//点数 边数
int n, index;
while (cin >> n)
{
if (n == 0)
break;
cin >> index;
for (int i = 0; i < index; i++)
cin >> edges[i].stN >> edges[i].edN >> edges[i].cost;
int ans = krukal(n, index);
cout << ans << endl;
}
return 0;
}
C Building a Space Station(POJ2031)
题目大意:
求对立体中求得最小生成树。
思路:
先处理出来边,两个球如果相交了那么代表这条边得权值为0。然后求所有边得最小生成树就行了。
AC代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int max_n = 1e4;
struct pp
{
int index;
double x, y, z, r;
};
double cost(pp p1, pp p2)
{
double ans = sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y) + (p2.z - p1.z) * (p2.z - p1.z)) - (p1.r + p2.r);
return ans > 0 ? ans : 0;
}
int parent[max_n];
vector<pp> vp;
struct edge
{
int stN, edN;
double co;
bool operator<(edge e)
{
return co - e.co < 0;
}
} edges[max_n];
void init(int n)
{
for (int i = 0; i < n; i++)
parent[i] = i;
}
int find(int x)
{
if (x != parent[x])
parent[x] = find(parent[x]);
return parent[x];
}
void unit(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
return;
parent[x] = y;
}
bool same(int x, int y)
{
return find(x) == find(y);
}
int main()
{
int n;
while (cin >> n)
{
if (n == 0)
break;
vp.clear();
for (int i = 0; i < n; i++)
{
pp tmp;
cin >> tmp.x >> tmp.y >> tmp.z >> tmp.r;
tmp.index = i;
vp.push_back(tmp);
}
int index = 0;
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
edges[index].stN = vp[i].index;
edges[index].edN = vp[j].index;
edges[index].co = cost(vp[i], vp[j]);
index++;
}
}
init(n);
sort(edges, edges + index);
double res = 0;
int nedge = 0;
for (int i = 0; i < index && nedge != n - 1; i++)
{
edge e = edges[i];
if (!same(e.stN, e.edN))
{
unit(e.stN, e.edN);
res += e.co;
nedge++;
}
}
printf("%.3lf\n", res);
}
return 0;
}
D Constructing Roads(POJ2421)
题目大意:
给你一个邻接矩阵并且已知某些点已经连接好了,求最小生成树。
思路:
已经连好的点可以直接加入到并查集里面或者将 权值设为零。
AC代码
#include <iostream>
#include <algorithm>
using namespace std;
int arr[200][200];
struct edge
{
int stn, edn, cost;
bool operator<(edge e)
{
return cost < e.cost;
}
} edges[10010];
int parent[200];
void init(int n)
{
for (int i = 1; i <= n; i++)
parent[i] = i;
}
int find(int x)
{
if (x != parent[x])
parent[x] = find(parent[x]);
return parent[x];
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void unit(int x, int y)
{
x = find(x);
y = find(y);
if (x != y)
parent[x] = y;
}
int kruskal(int n, int index)
{
int nedge = 0;
init(n);
sort(edges, edges + index);
int ans = 0;
for (int i = 0; i < index && nedge < n - 1; i++)
{
if (!same(edges[i].stn, edges[i].edn))
{
ans += edges[i].cost;
unit(edges[i].stn, edges[i].edn);
}
}
return ans;
}
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> arr[i][j];
int index = 0;
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
{
edges[index].stn = i;
edges[index].edn = j;
edges[index].cost = arr[i][j];
index++;
}
}
int m;
cin >> m;
for (int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
edges[index].stn = a;
edges[index].edn = b;
edges[index].cost = 0;
index++;
}
int ans = kruskal(n, index);
cout << ans << endl;
return 0;
}
E Truck History(POJ1789)
题目大意:
已知n个长度为7的字符串,字符串与字符串之间的权值是字符串不同字符的个数。求最小生成树。
思路:
先预处理出来权值,在模板。
AC代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
const int max_n = 2000 * 2000 + 10;
string ss[2020];
struct edge
{
int stn, edn, cost;
bool operator<(edge e)
{
return cost < e.cost;
}
} ve[max_n];
int getCost(string a, string b)
{
int sum = 0;
for (int i = 0; i < 7; i++)
{
if (a[i] != b[i])
sum++;
}
return sum;
}
int parent[2010];
void init(int n)
{
for (int i = 0; i < n; i++)
parent[i] = i;
}
int find(int x)
{
if (x != parent[x])
parent[x] = find(parent[x]);
return parent[x];
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void unit(int x, int y)
{
x = find(x);
y = find(y);
if (x != y)
parent[x] = y;
}
int kruskal(int n, int index)
{
int nedge = 0;
init(n);
sort(ve, ve + index);
int ans = 0;
for (int i = 0; i < index && nedge < n - 1; i++)
{
if (!same(ve[i].stn, ve[i].edn))
{
ans += ve[i].cost;
unit(ve[i].stn, ve[i].edn);
nedge++;
}
}
if (nedge < n - 1)
ans = -1;
return ans;
}
int main()
{
int n;
while (cin >> n)
{
if (n == 0)
break;
for (int i = 0; i < n; i++)
cin >> ss[i];
int index = 0;
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
ve[index].stn = i;
ve[index].edn = j;
ve[index].cost = getCost(ss[i], ss[j]);
index++;
}
}
int ans = kruskal(n, index);
cout << "The highest possible quality is 1/" << ans << "." << endl;
}
return 0;
}
F Arctic Network(POJ2349)
题目大意:
有n个前哨,使用无线电通信或者卫星都可以将他们联通。卫星无视距离。
现给出卫星的数目和每个前哨的坐标,让求两前哨可以无线电通信的最小权值。
思路:
先预处理所有的边,然后求最小生成树。
求的同时记录所有的权值,求完之后排序将大的边用卫星,剩下边的最大值就是所求。
对边的处理上,刚开始不用直接开方,可以都用整型的,最后输出答案时再开方。
AC代码
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int max_n = 500 * 500 + 10;
struct edge
{
int stn, edn, cost;
bool operator<(edge e)
{
return cost < e.cost;
}
} edges[max_n];
pair<int, int> pii[1000];
int parent[1000];
void init(int n)
{
for (int i = 0; i < n; i++)
parent[i] = i;
}
int find(int x)
{
if (x != parent[x])
parent[x] = find(parent[x]);
return parent[x];
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void unit(int x, int y)
{
x = find(x);
y = find(y);
if (x != y)
parent[x] = y;
}
int ans[1000];
bool cmp(int a, int b)
{
return a > b;
}
void kruskal(int n, int index, int s)
{
init(n);
sort(edges, edges + index);
int nedge = 0;
for (int i = 0; i < index && nedge < n - 1; i++)
{
if (!same(edges[i].stn, edges[i].edn))
{
unit(edges[i].stn, edges[i].edn);
ans[nedge] = edges[i].cost;
nedge++;
}
}
sort(ans, ans + nedge, cmp);
printf("%.2lf\n", sqrt(1.0 * ans[s - 1]));
}
int getCost(pair<int, int> p1, pair<int, int> p2)
{
return (p1.first - p2.first) * (p1.first - p2.first) + 1.0 * (p1.second - p2.second) * (p1.second - p2.second);
}
int main()
{
int T;
cin >> T;
while (T--)
{
int s, p;
cin >> s >> p;
for (int i = 0; i < p; i++)
cin >> pii[i].first >> pii[i].second;
int index = 0;
for (int i = 0; i < p - 1; i++)
{
for (int j = i + 1; j < p; j++)
{
edges[index].stn = i;
edges[index].edn = j;
edges[index].cost = getCost(pii[i], pii[j]);
index++;
}
}
kruskal(p, index, s);
}
return 0;
}
G Highways(POJ1751)
题目大意:
给出n个点的坐标,其中m个点已经联通了,求其最小生成树剩余的联通情况(即哪个点和哪个点联通了)
思路:
预处理边,最小生成树。
如果已经联通,权值设为零。在最小生成树里面,联通两点的同时输出答案就行了。
AC代码
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
const int max_n = 750 * 750 + 10 + 1000;
pair<int, int> pii[max_n];
int getCost(pair<int, int> p1, pair<int, int> p2)
{
return (p1.first - p2.first) * (p1.first - p2.first) + (p1.second - p2.second) * (p1.second - p2.second);
}
struct edge
{
int stn, edn, cost;
bool operator<(edge e)
{
return cost < e.cost;
}
} edges[max_n];
int parent[1000];
void init(int n)
{
for (int i = 1; i <= n; i++)
parent[i] = i;
}
int find(int x)
{
if (x != parent[x])
parent[x] = find(parent[x]);
return parent[x];
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void unit(int x, int y)
{
x = find(x);
y = find(y);
if (x != y)
parent[x] = y;
}
void kruskal(int n, int index)
{
init(n);
sort(edges, edges + index);
for (int i = 0; i < index; i++)
{
if (!same(edges[i].stn, edges[i].edn))
{
unit(edges[i].stn, edges[i].edn);
if (edges[i].cost != 0)
cout << edges[i].stn << " " << edges[i].edn << endl;
}
}
}
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> pii[i].first >> pii[i].second;
int index = 0;
for (int i = 1; i <= n - 1; i++)
{
for (int j = i + 1; j <= n; j++)
{
edges[index].stn = i;
edges[index].edn = j;
edges[index].cost = getCost(pii[i], pii[j]);
index++;
}
}
int m;
cin >> m;
for (int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
edges[index].stn = a;
edges[index].edn = b;
edges[index].cost = 0;
index++;
}
kruskal(n, index);
return 0;
}
H Til the Cows Come Home(POJ2387)
最短路板子题。可以直接上Dijkstra。
不过当时wa了好多发,可能时板子的问题,其实现在也不是很清楚
AC代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int max_v = 1e4 + 10;
int cost[max_v][max_v]; //权值
int d[max_v]; //从s出发最短距离
bool used[max_v]; //使用过的
int V; //顶点数
void dijkstra()
{
for (int i = 1; i <= V; i++)
{
used[i] = false;
d[i] = cost[1][i];
}
while (true)
{
int v = -1;
for (int u = 1; u <= V; u++)
{
if (!used[u] && (v == -1 || d[u] < d[v]))
v = u;
}
if (v == -1)
break;
used[v] = true;
for (int u = 1; u <= V; u++)
{
if (!used[u]) //注意要加这行!
d[u] = min(d[u], d[v] + cost[v][u]);
}
}
}
int main()
{
//点数 边数
int n, index;
while (cin >> index >> n)
{
V = n;
for (int i = 1; i <= V; i++)
{
for (int j = 1; j <= V; j++)
{
if (i != j)
cost[i][j] = 1 << 30;
else
cost[i][j] = 0;
}
}
for (int i = 0; i < index; i++)
{
int f, t, v;
cin >> f >> t >> v;
if (cost[f][t] > v)
{
cost[f][t] = v;
cost[t][f] = v;
}
}
dijkstra();
cout << d[n] << endl;
}
return 0;
}
I Frogger(POJ2253)
题目大意:
有n块石头的坐标,青蛙要从第1块跳到第2块。问青蛙最少跳多远就可以了(即求最短路中的最小权值)
思路:
因为数据量也不大,所以使用Floyd可以很好的解决这个问题。在更新路径的时候,如果I->K这段路大于I->J并且大于J-K,那么就应该选择I->J->K这条路让答案更小。
AC代码
#include <iostream>
#include <cmath>
#include <map>
#include <algorithm>
using namespace std;
const int max_n = 1e4;
double cost[max_n][max_n];
pair<int, int> pii[max_n];
double getCost(pair<int, int> p1, pair<int, int> p2)
{
return sqrt((double)(p1.first - p2.first) * (p1.first - p2.first) + (double)(p1.second - p2.second) * (p1.second - p2.second));
}
int main()
{
int n;
int index = 1;
while (cin >> n)
{
if (n == 0)
break;
for (int i = 0; i < n; i++)
cin >> pii[i].first >> pii[i].second;
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
cost[i][j] = getCost(pii[i], pii[j]);
cost[j][i] = cost[i][j];
}
}
for (int k = 0; k < n; k++)
{
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
// 如果ik kj 都小于ij 那么走“远路” i -> j - >k
if (cost[i][k] < cost[i][j] && cost[k][j] < cost[i][j])
{
//ij更新为远路中的最大值
if (cost[i][k] < cost[k][j])
cost[i][j] = cost[j][i] = cost[k][j];
else
cost[i][j] = cost[j][i] = cost[i][k];
}
}
}
}
cout << "Scenario #" << index << endl;
printf("Frog Distance = %.3f\n\n", cost[0][1]);
index++;
}
return 0;
}
J Heavy Transportation(POJ1797)
题目大意:
有n个点,给出一些边的权值,求最短路最小边的最大值。
思路:
直接dijkstra,每次选择有最大的那个点。注意最后让输出两个换行!!!
AC代码
#include <iostream>
#include <cmath>
#include <map>
#include <algorithm>
using namespace std;
const int max_n = 1e4;
int cost[max_n][max_n];
pair<int, int> pii[max_n];
int d[max_n]; //从s出发最短距离
bool used[max_n]; //使用过的
int V; //顶点数
//从s出发到各个顶点的最短距离
void dijkstra()
{
for (int i = 1; i <= V; i++)
{
used[i] = false;
d[i] = cost[1][i];
}
while (true)
{
int v = -1;
for (int u = 1; u <= V; u++)
{
if (!used[u] && (v == -1 || d[u] > d[v]))
v = u;
}
if (v == -1)
break;
used[v] = true;
for (int u = 1; u <= V; u++)
{
if (!used[u] && d[u] < min(d[v], cost[v][u]))
d[u] = min(d[v], cost[v][u]);
}
}
}
//最小边的最大值
int main()
{
int T;
cin >> T;
int index = 1;
while (T--)
{
int n, m;
cin >> n >> m;
V = n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cost[i][j] = 0;
}
}
for (int i = 0; i < m; i++)
{
int a, b, c;
cin >> a >> b >> c;
cost[a][b] = c;
cost[b][a] = c;
}
dijkstra();
cout << "Scenario #" << index << ":" << endl;
cout << d[n] << endl
<< endl;
index++;
}
return 0;
}
K Silver Cow Party(POJ3268)
题目大意:
有n头牛要从自己家农场去指定农场参加派对,结束之后再回自己家农场,问花费的最长时间时多少。
各个农场之间有向边连接。
思路:
很容易想到算出去时的最短路,和回去时的最短路,然后相加取最大值就行了。但是数据量1e3跑floyd会T,如果对每个点都跑一次dijkstra也会T。
参考网上一名大佬的思路。
跑一遍dijkstra之后,置换矩阵,变成两个单源最短路。
第一遍求i->x,第二遍求x->i。
置换矩阵可以让边的方向改变。很好的减少了时间复杂度。
AC代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int max_v = 1e4 + 10;
int cost[max_v][max_v]; //权值
int d[max_v]; //从s出发最短距离
int dx[max_v];
bool used[max_v]; //使用过的
int V; //顶点数
//从s出发到各个顶点的最短距离
int ans = 0;
void dxx(int x)
{
for (int i = 1; i <= V; i++)
{
used[i] = false;
dx[i] = cost[x][i];
}
while (true)
{
int v = -1;
for (int u = 1; u <= V; u++)
{
if (!used[u] && (v == -1 || dx[u] < dx[v]))
v = u;
}
if (v == -1)
break;
used[v] = true;
for (int u = 1; u <= V; u++)
{
if (!used[u])
dx[u] = min(dx[u], dx[v] + cost[v][u]);
}
}
}
void dijkstra(int s)
{
int sum = 0;
for (int i = 1; i <= V; i++)
{
used[i] = false;
d[i] = cost[s][i];
}
while (true)
{
int v = -1;
for (int u = 1; u <= V; u++)
{
if (!used[u] && (v == -1 || d[u] < d[v]))
v = u;
}
if (v == -1)
break;
used[v] = true;
for (int u = 1; u <= V; u++)
{
if (!used[u])
d[u] = min(d[u], d[v] + cost[v][u]);
}
}
}
int main()
{
//点数 边数
int n, m, x;
cin >> n >> m >> x;
V = n;
for (int i = 1; i <= V; i++)
{
for (int j = 1; j <= V; j++)
{
if (i != j)
cost[i][j] = 1 << 30;
else
cost[i][j] = 0;
}
}
for (int i = 0; i < m; i++)
{
int f, t, v;
cin >> f >> t >> v;
cost[f][t] = v;
}
dxx(x);
//矩阵置换,相当于把边反过来,那么从其他点到x的距离就是相当于是反过来后从x到其他点的距离
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
swap(cost[i][j], cost[j][i]);
}
dijkstra(x);
for (int i = 1; i <= n; i++)
ans = max(ans, dx[i] + d[i]);
cout << ans << endl;
return 0;
}
L Currency Exchange(POJ1860)
题目大意:
给出n种货币,给出m种汇率和佣金情况,问是否可以通过多次操作,是自己原本的钱增加,最后还得是原本的货币种类。
思路:
寻找一个环,使得钱到自己这里的时候变多了。寻找环很容易想到bellman ford的算法。需要注意的是,货币的数目需要实时更新。还有就是边的储存方式。
AC代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdlib>
using namespace std;
const int max_v = 1e3 + 10;
struct cur
{
//a->b的汇率 佣金 b->a的汇率 佣金
double rab, cab, rba, cba;
void operator=(cur c)
{
rab = c.rba;
cab = c.cba;
rba = c.rab;
cba = c.cab;
}
} curs[max_v][max_v];
struct edge
{
int from, to;
double cost;
};
edge es[max_v];
double d[max_v];
//v顶点数 e边数
int V, E;
int s;
double x;
bool find_negative_loop()
{
memset(d, 0, sizeof(d));
d[s] = x;
for (int i = 0; i < V; i++)
{
for (int j = 0; j < E; j++)
{
edge e = es[j];
e.cost = d[e.from] - (d[e.from] - curs[e.from][e.to].cab) * curs[e.from][e.to].rab;
if (d[e.to] < d[e.from] - e.cost)
{
d[e.to] = d[e.from] - e.cost;
if (i == V - 1) //存在负圈
return true;
}
}
}
return false;
}
int main()
{
//货币种类数量 货币兑换条目 N有的种类 N有的数量
int n, m;
//最后s->s
cin >> n >> m >> s >> x;
V = n;
int index = 0;
E = m;
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= n; j++)
{
curs[i][j].cab = curs[i][j].cba = (1 << 30);
curs[j][i] = curs[i][j];
}
}
for (int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
cin >> curs[a][b].rab >> curs[a][b].cab >> curs[a][b].rba >> curs[a][b].cba;
curs[b][a] = curs[a][b];
es[index].from = a;
es[index].to = b;
es[index].cost = 1 << 30;
index++;
es[index].from = b;
es[index].to = a;
es[index].cost = 1 << 30;
index++;
}
E = index;
if (find_negative_loop())
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
return 0;
}
M Wormholes(POJ3259)
题目大意:
有n个农场,m个路径和w个虫洞,走路需要花时间,但是经过虫洞可以回到过去。问转一圈能否让自己回到过去。
思路:
数据量比较小可以采用比较好写的floyd,判断d[i][i]是否小于0就行。
注意要使用较快的输入输出方式。如果是cin需要关闭输入输出流,不然会T。
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <stdio.h>
#define endl '\n'
using namespace std;
const int max_v = 1000 + 5;
const int INF = 0x3f3f3f3f;
int d[max_v][max_v];
int n, m, w;
bool floyd()
{
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (d[i][j] > d[i][k] + d[k][j])
d[i][j] = d[i][k] + d[k][j];
}
if (d[i][i] < 0)
return true;
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
memset(d, INF, sizeof(d));
cin >> n >> m >> w;
for (int i = 1; i <= n; i++)
d[i][i] = 0;
for (int i = 0; i < m; i++)
{
int a, b, c;
cin >> a >> b >> c;
if (d[a][b] > c)
d[a][b] = d[b][a] = c;
}
for (int i = 0; i < w; i++)
{
int a, b, c;
cin >> a >> b >> c;
d[a][b] = -c;
}
if (floyd())
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
N MPI Maelstrom(POJ1502)
题目大意:
给你邻接矩阵的下三角部分,x代表不连通,求最短路的最大边。
思路:
建议使用char*,这样可以直接使用atoi比较方便。
还有就是距离d要初始化为无穷大。这道题wa了20多发,因为没有初始化。
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <stdio.h>
using namespace std;
const int max_v = 200 + 5;
const int INF = 0x3f3f3f3f;
int d[max_v][max_v];
int a[max_v][max_v];
int main()
{
memset(d, INF, sizeof(d));
int n;
cin >> n;
for (int i = 2; i <= n; i++)
{
for (int j = 1; j < i; j++)
{
char s[100];
cin >> s;
if (s[0] == 'x')
a[j][i] = a[i][j] = INF;
else
a[j][i] = a[i][j] = atoi(s);
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (i == j)
d[i][j] = 0;
else
d[i][j] = a[i][j];
}
}
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
int ans = 0;
for (int i = 2; i <= n; i++)
ans = max(ans, d[1][i]);
cout << ans << endl;
return 0;
}
O Cow Contest(POJ3660)
题目大意:
有n头会编程的牛(震惊)牛的编程等级决定了它的排名(卷起来了)现在让判断能够确定排名的牛有几头。
思路:
数据量较小,使用floyd。如果a能够打败b,则设为1,反之为0。最后统计自己能打败的和能打败自己的牛的数目。如果正好的牛的总数-1,那么自己的排名就确定了。
AC代码
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace std;
const int max_n = 2e2;
const int INF = 0x3f3f3f3f;
int d[max_n][max_n];
int main()
{
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
d[a][b] = 1;
}
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
//i能打败k k能打败j 那么i能打败j
if (d[i][k] && d[k][j])
d[i][j] = 1;
}
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
int cnt = 0;
for (int j = 1; j <= n; j++)
{
//如果i能打败j 或者 j能打败i
if (d[i][j] || d[j][i])
cnt++;
}
if (cnt == n - 1)
ans++;
}
cout << ans << endl;
return 0;
}
P Arbitrage (POJ2240)
题目大意:
货币转换,问能否赚钱,和前面一道题很像。
思路:
使用map储存各个货币,将string变成int。初始化原本的钱为1,然后就直接floyd。需要注意的是,两个货币之间的传递需要汇率相乘。最后判断是否大于1就可以了。
AC代码
#include <iostream>
#include <string.h>
#include <algorithm>
#include <map>
#include <string>
using namespace std;
const int max_n = 1e3 + 100;
const int INF = 0x3f3f3f3f;
double d[max_n][max_n];
int main()
{
int index = 1;
int n;
while (cin >> n)
{
if (n == 0)
break;
memset(d, INF, sizeof(d));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (i == j)
d[i][j] = 1;
else
d[i][j] = INF;
}
}
map<string, int> mp;
for (int i = 0; i < n; i++)
{
string s;
cin >> s;
mp[s] = i;
}
int m;
cin >> m;
for (int i = 0; i < m; i++)
{
string s1, s2;
double r;
cin >> s1 >> r >> s2;
d[mp[s1]][mp[s2]] = r;
}
for (int k = 0; k < n; k++)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
d[i][j] = max(d[i][j], d[i][k] * d[k][j]);
}
}
}
cout << "Case " << index << ": ";
for (int i = 0; i < n; i++)
{
if (d[i][i] > 1)
{
cout << "Yes" << endl;
break;
}
else if (i == n - 1)
cout << "No" << endl;
}
index++;
}
return 0;
}
总结
本专题收获非常的多,对kruskal和dijkstra还有floyd的理解更深了。做的时候经常会“wc,这个算法还能这么用”这种,明白了很多个变形。
对邻接矩阵的掌握程度不错,但是邻接表就不太行了。以后还是要多加练习。