第二个专题了,初期图算法:
(1)、图的深度优先遍历和广度优先遍历
感觉这题比较好,就找了这题….
1、CF659E - New Reform
题意:给定一个有n个顶点,m条边的无向边,现要为每条边指定一个方向,使得separate cities 越少越好,一个城市如果如果他的入度为0的话,则其为separate cities 。
分析:分析下可知,如果一个图为树的话,那么任意规定一个点为根节点,遍历一次所有点方向即确定了,此时分离的城市数量为1,如果一个图存在环的话,那么此时分离城市的数量为0,此时选取环上一个与环外有边相连的点为根节点遍历图即可,此时任意点入度都大于等于1,所以数量为0,所以对于每一个连通分支,求得数量相加即可。
最基本的图的遍历,分为两种方式,dfs与bfs,此题运用到了dfs的思想,每次确定一个点与其邻接的点的方向即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 100010;
int vis[N], head[N];
int cnt, flag, k;
struct edge{
int v, next;
edge(int v, int n) : v(v), next(n) {}
edge() {}
}es[M<<1];
void add(int u, int v) { es[k] = edge(v, head[u]), head[u] = k++;}
void dfs(int x, int pre) {
vis[x] = cnt;
for (int i = head[x]; i != -1; i = es[i].next) {
int v = es[i].v;
if (vis[v] == cnt && v != pre) flag = 0;
if (!vis[v]) dfs(v, x);
}
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
memset(head+1, -1, 4*n);
for (int i = 0; i < m; i++) {
int u, v;
scanf("%d %d", &u, &v);
add(u, v); add(v, u);
}
int ans = 0;
for (int i = 1; i <= n; i++)
if (!vis[i]) {
flag = 1; ++cnt;
dfs(i, i); ans += flag;
}
printf("%d\n", ans);
return 0;
}
(2)、最短路径算法
1、poj1860
题意:给定N种货币,某些货币之间可以相互兑换,现在给定一些兑换规则,问能否从某一种货币开始兑换,经过一些中间货币之后,最后兑换回这种货币,并且得到的钱比之前的多。
分析:明显的判环,利用spfa即可,如果一个点入队超过n次,那么则存在环。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
const int N = 110;
struct currency {
int t;
double r, c;
currency(int t = 0, double r = 0, double c = 0) : t(t), r(r), c(c) {}
};
vector<currency> G[N];
double d[N];
int len, s, n, m;
queue<int> q;
int vis[N], num[N];
bool find_loop() {
q.push(s); num[s]++;
while (!q.empty()) {
int x = q.front(); q.pop();
vis[x] = 0;
for (int i = 0; i < G[x].size(); i++) {
currency e = G[x][i];
double t = (d[x] - e.c) * e.r;
if (t > d[e.t]) {
d[e.t] = t;
if (!vis[e.t]) {
vis[e.t] = 1; q.push(e.t);
if (++num[e.t] > n) return 1;
}
}
}
}
return 0;
}
int main() {
double t;
scanf("%d %d %d %lf", &n, &m, &s, &t);
d[s] = t;
for (int i = 0; i < m; i++) {
int f, t;
double rft, cft, rtf, ctf;
scanf("%d %d %lf %lf %lf %lf", &f, &t, &rft, &cft, &rtf, &ctf);
G[f].push_back(currency(t, rft, cft));
G[t].push_back(currency(f, rtf, ctf));
}
puts(find_loop() ? "YES" : "NO");
return 0;
}
2、poj3259
题意:虫洞问题,现在有n个点,m条边,代表现在可以走的通路,比如从a到b和从b到a需要花费c时间,现在在地上出现了w个虫洞,虫洞的意义就是你从a到b花费的时间是-c(时间倒流,并且虫洞是单向的),现在问你从某个点开始走,能否回到从前。
分析:上题判正环,这题建图后判负环。
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int N = 510, M = 5210;
const int INF = 0x3f3f3f3f;
int vis[N], head[N], cnt[N], d[N];
int k;
struct edge{
int v, d, next;
edge(int v, int d, int n) : v(v), d(d), next(n) {}
edge() {}
}es[M<<1];
void add(int u, int v, int d) {
es[k] = edge(v, d, head[u]);
head[u] = k++;
}
queue<int> q;
void init(int n) {
k = 0;
memset(head+1, -1, 4*(n+5));
memset(cnt+1, 0, 4*(n+5));
memset(vis+1, 0, 4*(n+5));
memset(d+1, INF, 4*(n+5));
while (!q.empty()) q.pop();
}
bool solve(int s, int n) {
d[s] = 0; cnt[s]++;
q.push(s);
while (!q.empty()) {
int t = q.front(); q.pop();
vis[t] = 0;
for (int i = head[t]; i != -1; i = es[i].next) {
int v = es[i].v;
if (d[v] > d[t] + es[i].d) {
d[v] = d[t] + es[i].d;
if (!vis[v]) {
vis[v] = 1; q.push(v);
if (++cnt[v] > n) return 1;
}
}
}
}
return 0;
}
int main() {
int f, n, m, w, s;
scanf("%d", &f);
while (f--) {
scanf("%d %d %d", &n, &m, &w);
init(n);
int u, v, d;
for (int i = 0; i < m; i++) {
scanf("%d %d %d", &u, &v, &d);
add(u, v, d); add(v, u, d);
}
for (int i = 0; i < w; i++) {
scanf("%d %d %d", &u, &v, &d);
add(u, v, -d);
s = u;
}
puts(solve(s, n) ? "YES" : "NO");
}
return 0;
}
3、poj1062
题意:有N个物品,每个物品都有自己的价格,但同时某些物品也可以由其他的(可能不止一个)替代品,这些替代品的价格比较“优惠”,问怎么样选取可以让你的花费最少来购买到物品1。
分析:很明显是要求最短路,求自己到1号物品的最短路,不能直接利用dijstra求解,但是我们可以枚举最小等级,这样再利用dijstra求解,等级差超过m的点不考虑。这样找出最小的值就行了。但是这样感觉有点麻烦了,不如直接搜索。。。
但是,有个问题就是数据量不能太大,否则的话队列中加入的点太多,会超内存,即使题目中的这点数据量,把队列放到函数中都会错,可能栈溢出了吧,只能用全局的。。。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
const int N = 105;
struct edge{
int to, dis;
edge(int a = 0, int d = 0) : to(a), dis(d){}
};
struct node{
int id, d, mag, mig;
node(int i = 0, int d = 0, int n = 0, int m = 0) : id(i), d(d), mag(n), mig(m) {}
bool operator < (const node& x)const { return d > x.d; }
};
vector<edge> G[N];
int d[N], grade[N];
int n, m;
priority_queue<node> q;
int dijstra() {
for (int i = 1; i <= n; i++) q.push(node(i, d[i], grade[i], grade[i]));
while (!q.empty()) {
node t = q.top(); q.pop();
int x = t.id;
//printf("%d %d %d %d\n", x, t.d, t.mag, t.mig);
if (x == 1) return t.d;
for (int i = 0; i < G[x].size(); i++) {
edge e = G[x][i];
int a = t.mag, b = t.mig;
if (abs(grade[e.to] - a) > m || abs(grade[e.to] - b) > m) continue;
q.push(node(e.to, e.dis + t.d, max(a, grade[e.to]), min(b, grade[e.to])));
}
}
}
int main() {
scanf("%d %d", &m, &n);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d %d %d", &d[i], &grade[i], &x);
for (int j = 0; j < x; j+&