​ 寒假开始,最近一段时间开始了寒假训练。这段时间如果没有别的事情,争取每天多刷题。每天都抽点空写写题解,写写刷题收获。

POJ - 2387 Dijkstra

题意

​ 一个无向图,起点为1,终点为N的最短路,保证有解。

​ 一个最短路的裸题。。。直接上熟悉的Dijkstra代码,堆优化复杂度

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
//#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 2000 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}

int dis[maxn];
struct node {
    int v, w, next;
}e[maxn << 1];

int head[maxn], cnt, vis[maxn];

void init() {
    cnt = 0;
    memset(head, -1, sizeof(head));
    memset(vis, 0, sizeof(vis));
}

void add(int u, int v, int w) {
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt++;
}

int dijkstra(int s, int ed) {
    memset(dis, 0x3f, sizeof(dis));    
    priority_queue<pair<int, int> > q;
    dis[s] = 0;
    q.push({0, s});
    while (!q.empty()) {
        int u = q.top().second;
        q.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        for (int i = head[u]; ~i; i = e[i].next) {
            int v = e[i].v, w = e[i].w;
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                q.push({-dis[v], v});
            }
        }
    }
    return dis[ed];
}


int main(void) {
    FAST_IO;
    init();
    int t, n;
    cin >> t >> n;
    for (int i = 0; i < t; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
    }
    cout << dijkstra(1, n) << endl;
    return 0;
}

POJ - 3660 Floyd

题意

​ 有n个牛打架,已经知道了m条打架结果,求出可以得到具体名称牛的个数。

分析

​ 当看到确定排名的时候,第一个想到的就是拓扑排序。虽然感觉并查集找连通个数,然后拓扑排序也可以AC。但这题在最短路专题里,就去想了想最短路的方法。

​ 若是已知 a与b有关系,b与c有关系,则可以推出a与c有关系。例如 a//b b//c 则 a//c 。这就是一种传递的关系,离散数学上的传递闭包。

​ 根据Floyd公式,则设表示,则可以推出闭包公式

void Floyd()
{
    for(int k=0; k<=n; ++k)
        for(int i=0; i<=n; ++i)
            for(int j=0; j<=n; ++j)
                a[i][j] = a[i][j] || (a[i][k] && a[k][j]);
}

​ 而当对象确定关系的个数总和,及,则表示的结果可以确定。

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
//#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 100 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}

int a[maxn][maxn];

int main(void) {
    FAST_IO;

    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        a[u][v] = 1;
    }
    // 求传递闭包
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (a[i][k] && a[k][j]) {
                    a[i][j] = 1;
                }
            }
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int temp = 0;
        for (int j = 1; j <= n; j++) {
            if (a[i][j] || a[j][i]) temp++;
        }
        // 若确定关系个数为n-1个,则排名确定
        if (temp == n - 1) ans++;
    }
    cout << ans << endl;

    return 0;
}

HDU - 2612 两次BFS

题意

​ Y和M要去同一家KFC见面。要去求出Y和M到KFC最少的总时间。

分析

​ 一个二维平面最短路问题。其实就是个有点麻(s)烦(b)的。方法有很多,可以跑两次BFS,找出Y和M都可以到达的KFC中总用时最短的。

​ 我的解法类似,但不想写2个函数,就用一个标记表示是Y还是M的路径。使用一个队列完成两次BFS。

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
//#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 200 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}

int n, m;
int vis1[maxn][maxn];
int vis2[maxn][maxn];
int mp2[maxn][maxn];
char mp[maxn][maxn];
int dis1[maxn][maxn], dis2[maxn][maxn];

struct node {
    int x, y, step, v;
    node(int x = 0, int y = 0, int step = 0, int v = 0) : x(x), y(y), step(step), v(v) { }
};

int const dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};

int x1, x2, y1, y2;
void bfs() {
    queue<node> q;
    q.push(node(x1, y1, 0, 1));
    q.push(node(x2, y2, 0, 2));
    while (!q.empty()) {
        node tq = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int tx = tq.x + dir[i][0];
            int ty = tq.y + dir[i][1];
            if (tx >= 0 && tx < n && ty >= 0 && ty < m && mp[tx][ty] != '#') {
                if (tq.v == 1) {
                    if (vis1[tx][ty]) continue;
                    q.push(node(tx, ty, tq.step + 1, 1));      
                    vis1[tx][ty] = 1;  
                    dis1[tx][ty] = tq.step + 1;        
                } else {
                    if (vis2[tx][ty]) continue;
                    q.push(node(tx, ty, tq.step + 1, 2));      
                    vis2[tx][ty] = 1; 
                    dis2[tx][ty] = tq.step + 1;
                }
            }
        }
    }
}

int main(void) {
    FAST_IO;

    while (cin >> n >> m) {
        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 < n; i++) {
            cin >> mp[i];
            for (int j = 0; j < m; j++) {
                if (mp[i][j] == 'Y') {
                    x1 = i, y1 = j;
                }
                if (mp[i][j] == 'M') {
                    x2 = i, y2 = j;
                }
            }
        }
        vis1[x1][y1] = vis1[x2][y2] = 1;
        vis2[x1][y1] = vis2[x2][y2] = 1;
        dis1[x1][y1] = 0;
        dis2[x2][y2] = 0;
        bfs();
        int ans = INF;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (mp[i][j] == '@' && dis1[i][j] != INF && dis2[i][j] != INF && dis1[i][j] + dis2[i][j] <= ans) {
                    ans = dis1[i][j] + dis2[i][j];
                }
            }
        }
        cout << ans * 11 << endl;
    }


    return 0;
}

HDU - 1875 MST

题意

​ 有N个岛屿,现在需要施工,要求让N个岛屿全都连通,并且2个小岛之间的距离不能小于10米,也不能大于1000米,并且花费最小。若是无法实现,则输出"oh!"。

分析

​ 还是最小生成树的裸题,是需要判断岛的距离大于等于10小于等于1000就行了。

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
//#include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 100 * 100 + 10;
const int INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}

struct point {
    int x, y;
    point(const int &x = 0, const int &y = 0) : x(x), y(y) { }
}p[maxn];

double dis(point &x, point &y) {
    return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}

struct node {
    int u, v;
    double w;
    bool operator<(const node &x) const {
        return w < x.w;
    }
}e[maxn];

int fa[maxn];
int cnt, setnum;

bool check(const double &w1) {
    if (w1 < 10 || w1 > 1000) return false;
    return true;
}

int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}

bool unio(int x, int y) {
    int fx = find(x), fy = find(y);
    if (fx != fy) {
        fa[fx] = fy;
        return false;
    }
    return true;
}

void init(int n) {
    cnt = 0;
    setnum = n;
    for (int i = 0; i <= n; i++) {
        fa[i] = i;
    }
}

double kruskal() {
    sort(e, e + cnt);
    double ans = 0;
    for (int i = 0; i < cnt; i++) {
        if (!unio(e[i].u, e[i].v)) {
            setnum--;
            ans += e[i].w;
        }
    }
    return ans;
}

int main(void) {
    FAST_IO;

    int t;
    cin >> t;
    while(t--) {
        int n;
        cin >> n;
        init(n);
        for (int i = 0; i < n; i++) {
            cin >> p[i].x >> p[i].y;
        }
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                double w = dis(p[i], p[j]);
                if (check(w)) {
                    e[cnt++] = {i, j, w};
                }
            }
        }
        double ans = kruskal();
        if (setnum != 1) cout << "oh!" << endl;
        else cout << fixed << setprecision(1) << ans * 100 << endl;
    }

    return 0;
}

POJ - 2018 二分

题意

​ 给定一个正整数数列,求一个平均数最大的,长度不小于L的子段。

分析

​ 蓝书上经典二分答案判定。

​ 若二分的值域为长度>=L子段的平均值,则可以把数组中的每个数字减去这个值,就转换成了"是否存在一个长度不小L的子段和>=0"的问题。

​ 而对于这个问题的判断,可以在的时间复杂度内实现。使用前缀和的性质,子段和可以转换成前缀和减法。即:

​ 随着i的增长,j的取值范围都没增大,所以只需要每次枚举j,记录最小的就行了。

​ 总复杂度:check函数,二分,总共为

AC代码

//#include <bits/stdc++.h>
#include <iostream>
#include <iomanip>
#include <bitset>
#include <string>
#include <set>
#include <stack>
#include <sstream>
#include <list>
// #include <array>
#include <vector>
#include <queue>
#include <map>
#include <memory>
#include <iterator>
#include <climits>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define all(x) (x).begin(), (x).end()
typedef long long ll;
typedef unsigned int UINT;
typedef unsigned long long ull;
typedef pair<int, int> pdi;
typedef pair<ll, int> pli;

int const maxn = 1e5 + 10;
inline int lc(int x)  {return x << 1;}
inline int rc(int x)  {return x << 1 | 1;}


double a[maxn], b[maxn], sum[maxn];
int main(void) {
    // FAST_IO;

    int n, L;
    // cin >> n >> L;
    scanf("%d %d", &n, &L);
    for (int i = 1; i <= n; i++) {
        // cin >> a[i];
        scanf("%lf", &a[i]);
    }
    double eps = 1e-5;
    double l = -1e6, r = 1e6;
    while (r - l > eps) {
        double mid = (l + r) / 2;
        for (int i = 1; i <= n; i++) {
            b[i] = a[i] - mid;
        }
        for (int i = 1; i <= n; i++) {
            sum[i] = (sum[i - 1] + b[i]);
        }
        double ans = -1e10;
        double min_val = 1e10;
        for (int i = L; i <= n; i++) {
            min_val = min(min_val, sum[i - L]);
            ans = max(ans, sum[i] - min_val);
        }
        if (ans >= 0) l = mid;
        else r = mid;
    }
    // cout << (int)(r * 1000) << endl;
    printf("%d\n", (int)(r * 1000));

    return 0;
}

总结

​ 今天寒假开始第一天,写了一些题。但大多还是都是水题、裸题。。。就当状态康复吧。

​ 今天这些题中,比较的经典的:floyd求传递闭包、二分答案求子段和,这两题还是挺具有代表性,比较增长视野的。