ACM 图论入门题(附代码解释)

目录

HDU 1869 六度分离

HDU 1874 畅通工程续 (最短路)

HDU 3339 In Action (最短路+01背包)

HDU 1162 Eddy's picture(prime算法)

HDU 1863 畅通工程 (最小生成树)

 HDU 1301 Jungle Roads (最小生成树)

POJ 3522 Slim Span (最小生成树)

HDU 1102 Constructing Roads (最小生成树)

HDU 1213 How Many Tables (并查集)

HDU1281 棋盘游戏 (二分匹配)

HDU1272 小希的迷宫(并查集)

HDU 4619 Warm up 2 (二分匹配)

HDU 2647 Reward (拓扑排序)

HDU 1596 find the safest road(迪杰斯特拉)

HDU 3829 Cat VS Dog (最大独立集)

HDU 1845 Jimmy’s Assignment (二分匹配)

HDU 1054 Strategic Game(最小顶点覆盖)

HDU 1598 find the most comfortable road(并查集+枚举)

HDU 1599 find the mincost route(Floyed找最小环)

HDU 1595 find the longest of the shortest(最短路的最坏情况)

POJ 2060 Taxi Cab Scheme(最小点覆盖)

HDU 1232 畅通工程(并查集)


HDU 1869 六度分离

题解:这个题是最短路的模板题,做法有很多,这里直接给出最暴力的做法,弗洛伊德。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<cmath>

const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9 + 10;
#define me(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
int mp[105][105];

int main() {
    int m, n, x, y;
    while (cin >> n >> m) {
        fill(mp[0], mp[0] + 105 * 105, inf);
        for (int i = 0; i <= n; i++)
            mp[i][i] = 0;
        for (int i = 0; i < m; i++) {
            scanf("%d%d", &x, &y);
            mp[x][y] = mp[y][x] = 1;
        }
        for (int k = 0; k < n; k++)
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    mp[i][j] = min(mp[i][k] + mp[k][j], mp[i][j]);
        int flog = 1;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                if (mp[i][j] > 7) {
                    flog = 0;
                    break;
                }
        if (flog)
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

 

HDU 1874 畅通工程续 (最短路)

题解:一个很裸的求最短路的题,这里用的方法是优先队列优化的Dij算法。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<cmath>

const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
int m, n, x, y, d[520];

struct node {
    int y, w;

    node(int a, int b) {
        y = a, w = b;
    }

    bool friend operator<(node a, node b) {
        return a.w < b.w;
    }
};

vector<node> mp[520];

void Dj(int x) {
    fill(d, d + 520, inf);
    d[x] = 0;
    priority_queue<node> q;
    q.push(node(x, d[x]));
    while (!q.empty()) {
        node now = q.top();
        q.pop();
        for (int i = 0; i < mp[now.y].size(); i++) {
            node next = mp[now.y][i];
            if (d[next.y] > now.w + next.w) {
                d[next.y] = now.w + next.w;
                q.push(node(next.y, d[next.y]));
            }
        }
    }
}

int main() {
    while (cin >> n >> m) {
        int a, b, x;
        for (int i = 0; i <= n; i++)
            mp[i].clear();
        for (int i = 0; i < m; i++) {
            scanf("%d%d%d", &a, &b, &x);
            mp[a].push_back(node(b, x));
            mp[b].push_back(node(a, x));
        }
        cin >> x >> y;
        Dj(x);
        if (d[y] == inf)
            cout << "-1" << endl;
        else
            cout << d[y] << endl;
    }
    return 0;
}

 

HDU 3339 In Action (最短路+01背包)

题意:有若干发电站,有无数的坦克,每个发电站有一个发电量,每个坦克能到达的发电站的发电量要达到总的发电量的一半以上,这个发射装置才能被摧毁,否则不能。

题解:到达每个发电站都有一段距离,坦克的耗油量就是这段距离,现在要求费最少的油量来摧毁这个发射装置,首先是求,基地能到达的每个发电站的最短路,然后用01背包求出消耗的最少油量。要是所能到达的发电站产生的电量都没有超过总产电量的一半,则无法摧毁。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>

const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;

struct node {
    int y, w;

    node(int yy, int ww) : y(yy), w(ww) {}
};

int n, m, p[maxn], d[maxn];
bool vis[maxn];
vector<node> a[maxn];

void Dij(int s) {
    fill(d, d + maxn, inf);
    me(vis, 0);
    d[s] = 0;
    for (int i = 0; i <= n; i++) {
        int mi = inf, u = -1;
        for (int j = 0; j <= n; j++)
            if (!vis[j] && mi > d[j]) {
                u = j;
                mi = d[j];
            }
        if (u == -1)
            return;
        vis[u] = 1;
        for (int j = 0; j < a[u].size(); j++) {
            int v = a[u][j].y;
            if (!vis[v] && d[u] + a[u][j].w < d[v])
                d[v] = a[u][j].w + d[u];
        }
    }
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        for (int i = 0; i <= n; i++)
            a[i].clear();
        for (int i = 0; i < m; i++) {
            int x, y, w;
            scanf("%d%d%d", &x, &y, &w);
            a[x].push_back(node(y, w));
            a[y].push_back(node(x, w));
        }
        Dij(0);
        int sam = 0, sum = 0;
        for (int i = 1; i <= n; i++) {
            cin >> p[i];
            sam += p[i];
            if (d[i] != inf)
                sum += d[i];
        }
        int dp[10005];
        me(dp, 0);
        for (int i = 1; i <= n; i++)
            for (int j = sum; j >= d[i]; j--)
                dp[j] = max(dp[j], dp[j - d[i]] + p[i]);
        int flog = 0, x;
        for (int i = 0; i <= sum; i++)
            if (dp[i] > sam / 2.0) {
                x = i;
                flog = 1;
                break;
            }
        if (flog)
            cout << x << endl;
        else
            cout << "impossible" << endl;
    }
    return 0;
}

 

HDU 1162 Eddy's picture(prime算法)

题解:其实就是一个最小生成树的问题,就是让所有点都连接,并且路径最小,用prime算法就可以了。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>

const int maxn = 1e2 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
double maps[maxn][maxn], sum;
int n;
bool vis[maxn];
struct node {
    double x, y;
} a[maxn];

double prime() {
    double d[maxn];
    fill(d, d + maxn, inf);
    d[1] = 0;
    me(vis, 0);
    for (int i = 1; i <= n; i++) {
        int u = -1;
        double ma = inf;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && d[j] < ma)
                ma = d[j], u = j;
        if (u == -1)
            return -1;
        vis[u] = 1, sum += ma;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && maps[u][j] < d[j])
                d[j] = maps[u][j];
    }
    return sum;
}

int main() {
    while (~scanf("%d", &n) && n) {
        for (int i = 1; i <= n; i++)
            scanf("%lf%lf", &a[i].x, &a[i].y);
        fill(maps[0], maps[0] + maxn * maxn, inf), sum = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++) {
                int x = a[i].x - a[j].x, y = a[i].y - a[j].y;
                maps[i][j] = sqrt(x * x + y * y);
            }
        printf("%.2f\n", prime());
    }
    return 0;
}

 

HDU 1863 畅通工程 (最小生成树)

题解:裸的最小生成树,直接prime算法就解决。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>

const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;

struct node {
    int y, w;

    node(int y1, int w1) : y(y1), w(w1) {}
};

int n, m;
vector<node> mp[maxn];

int prime() {
    bool vis[maxn];
    me(vis, 0);
    int d[maxn], sum = 0;
    fill(d, d + maxn, inf);
    d[1] = 0;
    for (int i = 1; i <= m; i++) {
        int mi = inf, u = -1;
        for (int j = 1; j <= m; j++)
            if (!vis[j] && mi > d[j])
                u = j, mi = d[j];
        if (u == -1)
            return -1;
        sum += mi;
        vis[u] = 1;
        for (int j = 0; j < mp[u].size(); j++) {
            int v = mp[u][j].y;
            if (!vis[v] && mp[u][j].w < d[v])
                d[v] = mp[u][j].w;
        }
    }
    return sum;
}

int main() {
    while (cin >> n >> m && n) {
        me(mp, 0);
        for (int i = 0; i < n; i++) {
            int x, y, w;
            scanf("%d%d%d", &x, &y, &w);
            mp[x].push_back(node(y, w)), mp[y].push_back(node(x, w));
        }
        int s = prime();
        if (s == -1)
            cout << "?" << endl;
        else
            cout << s << endl;
    }
    return 0;
}

 

 HDU 1301 Jungle Roads (最小生成树)

题解:一个裸的最小生成树的题,但是输入的是字符,这个需要注意一下。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<cmath>

const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
int maps[maxn][maxn], d[maxn], n;
bool vis[maxn];

int prime() {
    fill(d, d + maxn, inf);
    d[0] = 0;
    int sum = 0;
    for (int i = 0; i < n; i++) {
        int u = -1, mi = inf;
        for (int j = 0; j < n; j++)
            if (!vis[j] && d[j] < mi) {
                mi = d[j];
                u = j;
            }
        if (u == -1)
            return -1;
        vis[u] = 1;
        sum += d[u];
        for (int j = 0; j < n; j++)
            if (!vis[j] && maps[u][j] < d[j])
                d[j] = maps[u][j];
    }
    return sum;
}

int main() {
    while (cin >> n && n) {
        string s, s1;
        int x, w;
        fill(maps[0], maps[0] + maxn * maxn, inf);
        for (int i = 0; i < n; i++)
            maps[i][i] = 0;
        for (int i = 1; i < n; i++) {
            cin >> s >> x;
            for (int j = 0; j < x; j++) {
                cin >> s1 >> w;
                maps[s1[0] - 'A'][s[0] - 'A'] = maps[s[0] - 'A'][s1[0] - 'A'] = w;
            }
        }
        me(vis);
        cout << prime() << endl;
    }
    return 0;
}

 

POJ 3522 Slim Span (最小生成树)

题解:题目非常长,但是意思很简单就是求最小生成树里的最大边的权值减去最小边的权值的差最小。可以先将边的权值从小到大排序,然后从里面选边构造最小生成树,每次构造后去掉一条边,知道无法再构造最小生成树,再从这些构造的树里面选取符合题意的。因为有些边已经构造好了,所以要用并查集判断在构造一条边是否构成环,若要构成环,则不加入这条边。
 

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>

const int maxn = 5e3 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
int f[maxn], n, m, ans;

struct node {
    int x, y, w;

    bool friend operator<(node a, node b) {
        return a.w < b.w;
    }
} a[maxn];

int find(int x) {
    if (x == f[x])
        return x;
    return f[x] = find(f[x]);
}

void dij(int x) {
    int sum = 0;
    for (int i = 1; i <= n; i++)
        f[i] = i;
    int mi = inf, ma = 0;
    for (int i = x; i <= m; i++) {
        int x1 = find(a[i].x);
        int y1 = find(a[i].y);
        if (x1 != y1) {
            mi = min(a[i].w, mi);
            ma = max(a[i].w, ma);
            f[x1] = y1;
            sum++;
        }
    }
    if (sum == n - 1)
        ans = min(ans, ma - mi);
}

int main() {
    while (cin >> n >> m && n + m) {
        for (int i = 1; i <= m; i++)
            scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].w);
        sort(a + 1, a + m + 1);
        ans = inf;
        for (int i = 1; m - i + 1 >= n - 1; i++)
            dij(i);
        if (ans == inf)
            cout << "-1" << endl;
        else
            cout << ans << endl;
    }
    return 0;
}

 

HDU 1102 Constructing Roads (最小生成树)

题解:也是一道最小生成树的题,现在有些点已经连接了,就不需要再建立点,直接可以在地图上将两点的距离赋值成0,然后去构造最小生成树。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>

const int maxn = 1e2 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
int maps[maxn][maxn], d[maxn], n, m;;
bool vis[maxn];

int prime() {
    fill(d, d + maxn, inf);
    d[1] = 0;
    for (int i = 1; i <= n; i++) {
        int mi = inf, s;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && d[j] < mi) {
                mi = d[j];
                s = j;
            }
        vis[s] = 1;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && maps[s][j] < d[j])
                d[j] = maps[s][j];
    }
    int sum = 0;
    for (int i = 1; i <= n; i++)
        sum += d[i];
    return sum;
}

int main() {
    while (~scanf("%d", &n)) {
        fill(maps[0], maps[0] + maxn * maxn, inf);
        me(vis);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                scanf("%d", &maps[i][j]);
        int k, x, y;
        cin >> k;
        for (int i = 0; i < k; i++) {
            scanf("%d%d", &x, &y);
            maps[x][y] = maps[y][x] = 0;
        }
        int s = prime();
        cout << s << endl;
    }
    return 0;
}

 

HDU 1213 How Many Tables (并查集)

题解:相互认识,或者间接认识的人可以坐一桌,因为合并后,只要相互认识或则间接认识的人的父节点都是一个,所以最后只要记录有多少不同的父节点就行了。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<cmath>

const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
int a[maxn], n, m;

int find(int x) {
    if (a[x] == x)
        return x;
    return a[x] = find(a[x]);
}

void un(int x, int y) {
    int x1 = find(x);
    int y1 = find(y);
    if (x1 != y1)
        a[x1] = y1;
}

int main() {
    while (cin >> n && n) {
        for (int i = 1; i <= n; i++)
            a[i] = i;
        cin >> m;
        while (m--) {
            int x, y;
            scanf("%d%d", &x, &y);
            un(x, y);
        }
        int s = 0;
        for (int i = 1; i <= n; i++)
            if (a[i] == i)
                s++;
        cout << s - 1 << endl;
    }
    return 0;
}

 

HDU1281 棋盘游戏 (二分匹配)

题解:因为车只能一排或者一列只能有一个,不然就会相互攻击。首先我们要求出这个棋盘最多可以放多少个棋。然后将能放棋的位置标记成不能放棋,再算一次最多能放多少棋子,要是数量减少了,说明当前点是重要点,每次标记后要还原。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>

const int maxn = 1e2 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
#define me(a, b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define mid (l+r)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
typedef long long ll;
using namespace std;
bool vis[maxn];
int n, m, k, maps[maxn][maxn];
int d[maxn];

int found(int u) {
    for (int i = 1; i <= m; i++) {
        if (!vis[i] && maps[u][i]) {
            vis[i] = 1;
            if (d[i] == -1 || found(d[i])) {
                d[i] = u;
                return 1;
            }
        }
    }
    return 0;
}

int hungry() {
    int sum = 0;
    me(d, -1);
    for (int i = 1; i <= n; i++) {
        me(vis, 0);
        sum += found(i);
    }
    return sum;
}

int main() {
    int Case = 1;
    while (scanf("%d%d%d", &n, &m, &k) != EOF) {
        me(maps, 0);
        for (int i = 0; i < k; i++) {
            int x, y;
            scanf("%d%d", &x, &y);
            maps[x][y] = 1;//标记可以放棋的位置
        }
        int t_max = hungry(), sum = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                if (maps[i][j]) {
                    maps[i][j] = 0;//标记
                    int temp = hungry();
                    if (temp < t_max)
                        sum++;
                    maps[i][j] = 1;//还原
                }
        printf("Board %d have %d important blanks for %d chessmen.\n", Case++, sum, t_max);
    }
    return 0;
}

 

HDU1272 小希的迷宫(并查集)

题解:这题其实就是考一个并查集,判断有无回路和是否连通,注意空图也要输出Yes。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>

const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int root[maxn], flog, n;
bool vis[maxn];

void inct() {
    for (int i = 1; i < maxn; i++)
        root[i] = i;
    me(vis, 0);
    flog = 0, n = 0;
}

int find(int x) {
    if (root[x] == x)
        return x;
    return root[x] = find(root[x]);
}

void un(int x, int y) {
    int x1 = find(x);
    int y1 = find(y);
    if (x1 != y1)
        root[y1] = x1;
    else
        flog = 1;///有回路
}

int main() {
    int x, y;
    inct();
    while (~scanf("%d%d", &x, &y) && !(x == -1 && y == -1)) {
        if (!x && !y) {
            int sum = 0;
            for (int i = 1; i <= n; i++)
                if (vis[i] && root[i] == i)
                    sum++;
            if (!flog && sum <= 1)///sum<1时就是空图的情况
                cout << "Yes" << endl;
            else
                cout << "No" << endl;
            inct();///每次结束都要初始化
        } else {
            vis[x] = vis[y] = 1;///可能有些点没有输进去,所以只判断输入的点是否连通
            n = max(n, max(x, y));
            un(x, y);
        }
    }
    return 0;
}

 

HDU 4619 Warm up 2 (二分匹配)

题解:一个比较裸的二分匹配问题。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>

const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int maps[maxn][maxn], v1[maxn][maxn], y[maxn], n, m;
bool vis[maxn];

bool found(int v) {
    for (int i = 0; i < m; i++)
        if (v1[v][i] && !vis[i]) {
            vis[i] = 1;
            if (y[i] == -1 || found(y[i])) {
                y[i] = v;
                return 1;
            }
        }
    return 0;
}

int angry() {
    me(y, -1);
    int sum = 0;
    for (int i = 0; i < n; i++) {
        me(vis, 0);
        sum += found(i);
    }
    return sum;
}

int main() {
    while (~scanf("%d%d", &n, &m) && m + n) {
        int x, y;
        me(maps, -1), me(v1, 0);
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &x, &y);
            maps[x][y] = maps[x + 1][y] = i;
        }
        for (int i = 0; i < m; i++) {
            scanf("%d%d", &x, &y);
            if (maps[x][y] != -1)
                v1[maps[x][y]][i] = 1;
            if (maps[x][y + 1] != -1)
                v1[maps[x][y + 1]][i] = 1;
        }
        cout << m + n - angry() << endl;
    }
    return 0;
}

 

HDU 2647 Reward (拓扑排序)

题解:拓扑排序,拓扑排序里面不能出现环,所以输入的数据里有出现环,就输出-1,例如,甲比乙高,乙又比甲高,这种情况直接输出-1,还有就是题中要求最少的花钱数。首先发钱最少的肯定是拓扑排序最后面的那一个,这就是求在最后一个人前有多少人,但是可能最后一个同时有两个都应该比他发的多的,那两个又可以发一样多的钱,所以这个不能只是普通的拓扑排序。所以我们可以反着来。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<string>

const int maxn = 1e4 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int a[maxn], cnt[maxn], n, m;
vector<int> s[maxn];

ll topu() {
    queue<int> q;
    int sum = 0;
    for (int i = 1; i <= n; i++)
        if (!cnt[i]) {
            a[i] = 888;
            q.push(i);
        }
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        sum++;
        for (int i = 0; i < s[x].size(); i++) {
            cnt[s[x][i]]--;
            if (cnt[s[x][i]] == 0) {
                q.push(s[x][i]);
                a[s[x][i]] = max(a[x] + 1, a[s[x][i]]);
            }
        }
    }
    if (sum < n)
        return 0;
    return 1;
}

int main() {
    while (cin >> n >> m) {
        for (int i = 1; i <= n; i++)
            s[i].clear();
        me(a, 0), me(cnt, 0);
        int x, y;
        for (int i = 0; i < m; i++) {
            scanf("%d%d", &y, &x);
            s[x].push_back(y);
            cnt[y]++;
        }
        if (topu()) {
            ll sum = 0;
            for (int i = 1; i <= n; i++)
                sum += a[i];
            cout << sum << endl;
        } else
            cout << "-1" << endl;
    }
    return 0;
}

 

HDU 1596 find the safest road(迪杰斯特拉)

题解:题意很清楚,用Dij算法就可以了,但是注意初始化。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>

const int maxn = 1e3 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int n, m;
double maps[maxn][maxn], d[maxn];
bool vis[maxn];

int Dij(int x) {
    me(d, 0.0);
    me(vis, 0);
    d[x] = 1.0;
    for (int i = 1; i <= n; i++) {
        int u = -1;
        double mi = 0;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && mi < d[j])
                u = j, mi = d[j];
        if (u == -1)
            return -1;
        vis[u] = 1;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && d[u] * maps[u][j] > d[j])
                d[j] = d[u] * maps[u][j];
        }
    }
}

int main() {
    while (~scanf("%d", &n)) {
        me(maps, 0);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                scanf("%lf", &maps[i][j]);
        cin >> m;
        while (m--) {
            int x, y;
            scanf("%d%d", &x, &y);
            int s = Dij(x);
            if (s == -1)
                cout << "What a pity!" << endl;
            else if (!d[y])
                cout << "What a pity!" << endl;
            else
                printf("%.3f\n", d[y]);
        }
    }
    return 0;
}

 

HDU 3829 Cat VS Dog (最大独立集)

题解:这里面只有猫和狗,一个人不喜欢猫,就喜欢狗。题上要求的是最大开心数目,我们可以反过来求最小不开心数目,然后用总人数减去不开心人数,就是答案。要求最少不开心人数,就可以用到二分匹配了,要是有两个人有不同喜欢的动物,那只能满足其中一个,所以我们把有不同喜欢动物的人可以标记一下,看最多有多少人有不同喜欢的动物,然后只能满足其中的一半,最后用总数减去另外一半就行了。
注意:最大独立集=顶点数-最大匹配。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>

const int maxn = 5e2 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int n, m, p, d[maxn];
bool vis[maxn];
vector<int> maps[maxn];

int found(int x) {
    for (int i = 0; i < maps[x].size(); i++) {
        int u = maps[x][i];
        if (!vis[u]) {
            vis[u] = 1;
            if (d[u] == -1 || found(d[u])) {
                d[u] = x;
                return 1;
            }
        }
    }
    return 0;
}

int hungry() {
    me(d, -1);
    int sum = 0;
    for (int i = 1; i <= p; i++) {
        me(vis, 0);
        sum += found(i);
    }
    return sum / 2;///因为在相互矛盾的人中,拿走一个其中一个不喜欢的另一个就会开心,所以说,会有一半的人开心,一半的人不开心。
}

int main() {
    while (~scanf("%d%d%d", &n, &m, &p)) {
        string a[maxn], b[maxn];
        me(maps, 0);
        for (int i = 1; i <= p; i++)
            cin >> a[i] >> b[i];
        for (int i = 1; i <= p; i++)
            for (int j = 1; j <= p; j++)
                if (i != j && !a[i].compare(b[j]) || i != j && !b[i].compare(a[j]))
                    maps[i].push_back(j), maps[j].push_back(i);
        cout << p - hungry() << endl;///最大独立集=顶点数-最大匹配
    }
    return 0;
}

 

HDU 1845 Jimmy’s Assignment (二分匹配)

题解:二分匹配,但是要卡时间,注意。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>

const int maxn = 1e4 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
bool vis[maxn];
int a[maxn], n;
vector<int> q[maxn];

bool found(int u) {
    for (int i = 0; i < q[u].size(); i++) {
        int x = q[u][i];
        if (!vis[x]) {
            vis[x] = 1;
            if (a[x] == -1 || found(a[x])) {
                a[x] = u;
                return 1;
            }
        }
    }
    return 0;
}

int hangry() {
    int sum = 0;
    me(a, -1);
    for (int i = 1; i <= n; i++) {
        me(vis, 0);
        sum += found(i);
    }
    return sum;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 1; i <= n; i++)
            q[i].clear();
        for (int i = 0; i < 3 * n / 2; i++) {
            int x, y;
            scanf("%d%d", &x, &y);
            q[x].push_back(y);
            q[y].push_back(x);
        }
        cout << hangry() / 2 << endl;
    }
    return 0;
}

 

HDU 1054 Strategic Game(最小顶点覆盖)

题解:二分匹配问题,最小顶点覆盖=最大二分匹配除以2。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>

const int maxn = 2e3 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int n, d[maxn];
bool vis[maxn];
vector<int> maps[maxn];

int found(int x) {
    for (int i = 0; i < maps[x].size(); i++) {
        int u = maps[x][i];
        if (!vis[u]) {
            vis[u] = 1;
            if (d[u] == -1 || found(d[u])) {
                d[u] = x;
                return 1;
            }
        }
    }

    return 0;
}

int hungry() {
    me(d, -1);
    int sum = 0;
    for (int i = 0; i < n; i++) {
        me(vis, 0);
        sum += found(i);
    }
    return sum;
}

int main() {
    while (~scanf("%d", &n)) {
        me(maps, 0);
        for (int i = 0; i < n; i++) {
            int x, m;
            scanf("%d: (%d)", &x, &m);
            while (m--) {
                int y;
                scanf("%d", &y);
                maps[x].push_back(y);
                maps[y].push_back(x);
            }
        }
        cout << hungry() / 2 << endl;
    }
    return 0;
}

 

HDU 1598 find the most comfortable road(并查集+枚举)

题解:首先按照边的速度从小到大排序,然后枚举。每次向集合中加入边,直到起点和终点在一个集合中,每次都要跟新最大差值。如果无法更新成功说明后面的边已经不能使起点和终点连接了,直接跳出。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>

const int maxn = 1e3 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int root[maxn], n, m;

struct node {
    int x, y, s;

    bool friend operator<(node a, node b) {
        return a.s < b.s;
    }
} a[maxn];

void inct() {
    for (int i = 1; i <= n; i++)
        root[i] = i;
}

int find(int x) {
    if (x == root[x])
        return x;
    return root[x] = find(root[x]);
}

void un(int x, int y) {
    int x1 = find(x), y1 = find(y);
    if (x1 != y1)
        root[y1] = x1;
}

int main() {
    while (~scanf("%d%d", &n, &m)) {
        for (int i = 0; i < m; i++)
            scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].s);
        sort(a, a + m);
        int t, ans;
        cin >> t;
        while (t--) {
            int x, y;
            scanf("%d%d", &x, &y);
            ans = inf;
            for (int i = 0; i < m; i++) {
                inct();
                int flog = 0, s = 0;
                for (int j = i; j < m; j++) {
                    un(a[j].x, a[j].y);
                    if (find(x) == find(y)) {
                        flog = 1, s = j;
                        break;
                    }
                }
                if (!flog)///没有路能够到达终点了
                    break;
                ans = min(ans, a[s].s - a[i].s);
            }
            if (ans == inf)
                cout << "-1" << endl;
            else
                cout << ans << endl;
        }
    }
    return 0;
}

 

HDU 1599 find the mincost route(Floyed找最小环)

题解:Floyed在松弛的时候,是不断更新两点之间的距离,如果这i j两点间接相连那么一定有一条路可以从i到j,那么对于一个点k,还没有对它进行松弛的时候,它与前面的点就是初始化的值,直接相连则为两点距离否则为inf无穷大。这时候你去枚举他前面所有的点,如果存在两个点i j满足  dis[i][j]+mp[j][k]+mp[k][i]小于inf;dis[i][j]不为inf说明 i j在之前已经被松弛过,,但是mp[j][k]和mp[k][i]只有在k和i j都直接相邻的情况下才不为inf,那就说明ijk三个点一定在一个最小环里面。
 

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>

const int maxn = 1e2 + 5;
const int mod = 1e9;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int maps[maxn][maxn], dist[maxn][maxn], n, m;

int Floyd() {
    int ans = inf;
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i < k; i++)
            for (int j = i + 1; j < k; j++)
                ans = min(ans, dist[i][j] + maps[i][k] + maps[k][j]);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
    }
    return ans;
}

void inct() {
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            if (i == j)
                maps[i][j] = maps[j][i] = dist[i][j] = dist[j][i] = 0;
            else
                maps[i][j] = maps[j][i] = dist[i][j] = dist[j][i] = inf;
        }
}

int main() {
    while (~scanf("%d%d", &n, &m)) {
        inct();
        for (int i = 0; i < m; i++) {
            int x, y, w;
            scanf("%d%d%d", &x, &y, &w);
            maps[x][y] = maps[y][x] = dist[x][y] = dist[y][x] = min(w, maps[x][y]);
        }
        int ans = Floyd();
        if (ans == inf)
            cout << "It's impossible." << endl;
        else
            cout << ans << endl;
    }
    return 0;
}

 

HDU 1595 find the longest of the shortest(最短路的最坏情况)

题解:一个比较经典的题,首先这个题不能全遍历一遍,那样肯定会超时的。正确做法是先记录最小路的路径,在每次删除最小路径的一条边,去最坏的那种情况,这也相当于一个剪枝吧,因为只要不是删除最短路径上的边,最短路永远都是那一条不会变,所以,我们只需要删除最短路上的边求最坏的情况就行了。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>

const int maxn = 1e3 + 5;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int maps[maxn][maxn], pre[maxn], n, m;

int Ku(int x) {
    int d[maxn], vis[maxn];
    me(vis, 0);
    fill(d, d + maxn, inf);
    d[1] = 0;///虽然后面要删除边,但是起点还是从1开始
    for (int i = 1; i <= n; i++) {
        int u = -1, mi = inf;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && mi > d[j])
                u = j, mi = d[j];
        vis[u] = 1;
        for (int j = 1; j <= n; j++)
            if (!vis[j] && d[u] + maps[u][j] < d[j]) {
                d[j] = d[u] + maps[u][j];
                if (x)///只记录最短路径上的边
                    pre[j] = u;
            }
    }
    return d[n];
}

int main() {

    while (~scanf("%d%d", &n, &m)) {
        fill(maps[0], maps[0] + maxn * maxn, inf);
        for (int i = 0; i < m; i++) {
            int x, y, w;
            scanf("%d%d%d", &x, &y, &w);
            maps[x][y] = maps[y][x] = min(maps[x][y], w);
        }
        me(pre, 0);
        int mi = Ku(1);
        for (int i = n; i != 1; i = pre[i]) {
            int x = maps[i][pre[i]];
            maps[i][pre[i]] = maps[pre[i]][i] = inf;
            mi = max(mi, Ku(0));
            maps[i][pre[i]] = maps[pre[i]][i] = x;
        }
        cout << mi << endl;
    }
    return 0;
}

 

POJ 2060 Taxi Cab Scheme(最小点覆盖)

题意:现在有个n辆车的出行表,现在要是有趟车在送完自己的乘客后,要是在另一趟车出发前赶到该趟车的出发点,另一辆车就可以不用跑,这样就可以减少一辆车。问:最少用几辆车可以完成运输。

题解:这个题是一个最小点覆盖问题,最小点覆盖 = | G | - 最大匹配数。所以我们求最大匹配数就行了。先提前处理,每辆车除了完成自己的运输,还能够帮哪些车完成运输,然后在求最大匹配就行了。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>

const int maxn = 5e2 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define mid (l+r)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI 3.14159265358979323846
int dir[4][2] = {0, -1, -1, 0, 0, 1, 1, 0};
typedef long long ll;
using namespace std;
bool vis[maxn];
int d[maxn], n;
int maps[maxn][maxn];
struct node {
    int t1, t2;///t1表示该趟车的出发时间,t2表示该趟车送完乘客后的时间
    int x1, x2, y1, y2;
} ti[maxn];

int dis_t(int x1, int y1, int x2, int y2)///计算在送乘客的过程中花的时间
{
    return abs(x1 - x2) + abs(y1 - y2);
}

int found(int u) {
    for (int i = 1; i <= n; i++)
        if (maps[u][i] && !vis[i]) {
            vis[i] = 1;
            if (d[i] == -1 || found(d[i])) {
                d[i] = u;
                return 1;
            }
        }
    return 0;
}

int hungry()///求最大匹配
{
    me(d, -1);
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        me(vis, 0);
        sum += found(i);
    }
    return sum;
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            int a, b;
            scanf("%d:%d", &a, &b);
            ti[i].t1 = a * 60 + b;///化成分钟数好计算
            scanf("%d%d%d%d", &ti[i].x1, &ti[i].y1, &ti[i].x2, &ti[i].y2);
            ti[i].t2 = ti[i].t1 + dis_t(ti[i].x1, ti[i].y1, ti[i].x2, ti[i].y2);
        }
        me(maps, 0);
        for (int i = 1; i <= n; i++)///判断在i趟车送完乘客后能不能在j趟车出发前赶到j趟车的出发点
            for (int j = 1; j <= n; j++)
                if (ti[i].t2 + dis_t(ti[i].x2, ti[i].y2, ti[j].x1, ti[j].y1) < ti[j].t1)
                    maps[i][j] = 1;
        printf("%d\n", n - hungry());///最小点覆盖 = | G | - 最大匹配数
    }
    return 0;
}

 

HDU 1232 畅通工程(并查集)

题解:首先将每个点的父节点都赋值为本身,然后通过并查集合并,合并后看有多少点的父节点还是本身,说明这下点没有和其他点连接在一起需要建立一条边。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<cmath>

const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
int a[maxn], n, m;

int find(int x) {
    if (a[x] == x)
        return x;
    return a[x] = find(a[x]);
}

void un(int x, int y) {
    int x1 = find(x);
    int y1 = find(y);
    if (x1 != y1)
        a[x1] = y1;
}

int main() {
    while (cin >> n && n) {
        for (int i = 1; i <= n; i++)
            a[i] = i;
        cin >> m;
        while (m--) {
            int x, y;
            scanf("%d%d", &x, &y);
            un(x, y);
        }
        int s = 0;
        for (int i = 1; i <= n; i++)
            if (a[i] == i)
                s++;
        cout << s - 1 << endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值