• 2013 Multi-University Training Contest 1


    HDU-4605 Magic Ball Game

    题意:给定一颗以1为根的数,每个节点要么有两个孩子节点,要么没有孩子,每个节点有一个重量,现在从节点1往下放置一个小球,根据小球和节点的重量的不同球落下的轨迹是一个概率问题:

    设球的重量为X,节点的重量为w[i]:
    X = w[i],那么小球的运动将停止;
    X < w[i],那么小球向左孩子下落的概率为1/2,向右孩子下落的概率为1/2;
    X > w[i],那么小球向左落下概率为1/8,向右落下的概率为7/8。

    现在有Q组询问,问小球的质量为X,落到v节点的概率为多大?

    分析:最直接的办法就是直接暴力求解该题,从询问的叶子节点开始向上寻找,进行概率的累加,比赛的时候这样写,超时了。赛后听说是使用的树状数组维护路径状态进行求解。具体过程是在一个dfs的过程中,统计好当前位置的左路径的节点和右路径的节点,然后将小球的质量在树状数组中进行查找,计算出比小球质量较小的节点数以及比小球质量较大的节点数,累加概率即可。注意直接dfs会爆栈,使用编译器命令后解决该问题。

    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <map>
    #pragma comment(linker, "/STACK:1024000000,1024000000")
    using namespace std;
    
    struct Node {
        int num, vertex, weight, ansx, ansy;
        Node(int _n, int _v, int _w, int _ansx, int _ansy) : 
        num(_n), vertex(_v), weight(_w), ansx(_ansx), ansy(_ansy) {}
        bool operator < (const Node &other) const {
            return num < other.num;
        }
    };
    
    const int N = 100005;
    vector<Node>v[N], vv;
    // first元素是询问的编号,pair的第一个元素是询问的 
    int ch[N][2], w[N];
    int n, m;
    int num[N<<1], cnt;
    map<int,int>mp;
    int lbit[N<<1], rbit[N<<1]; // 因为询问和给定节点加起来上限是2*N个不同的数值 
    
    inline int lowbit(int x) {
        return x & -x;
    }
    
    void add(int bit[], int x, int val) {
        for (int i = x; i <= cnt; i += lowbit(i)) {
            bit[i] += val;
        }
    }
    
    int sum(int bit[], int x) {
        int ret = 0;
        for (int i = x; i > 0; i -= lowbit(i)) {
            ret += bit[i];
        }
        return ret;
    }
    
    void dfs(int u) {
        for (int i = 0; i < (int)v[u].size(); ++i) {
            int weight = mp[v[u][i].weight];
            int lsum = sum(lbit, weight), rsum = sum(rbit, weight);
            int ltot = sum(lbit, cnt), rtot = sum(rbit, cnt);
            bool find = lsum && bool(lsum - sum(lbit, weight-1)) || rsum && bool(rsum - sum(rbit, weight-1));
            if (!find) {
                v[u][i].ansx = rsum;
                v[u][i].ansy = rtot-rsum + ltot-lsum + lsum*3 + rsum*3;
            } else {
                v[u][i].ansx = v[u][i].ansy = -1;
            }
        }
        if (ch[u][0]) {
            add(lbit, mp[w[u]], 1);
            dfs(ch[u][0]);
            add(lbit, mp[w[u]], -1);
        }
        if (ch[u][1]) {
            add(rbit, mp[w[u]], 1);
            dfs(ch[u][1]);
            add(rbit, mp[w[u]], -1);
        }
    }
    
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
            scanf("%d", &n);
            cnt = 0, mp.clear(), vv.clear();
            memset(ch, 0, sizeof (ch));
            memset(lbit, 0, sizeof (lbit));
            memset(rbit, 0, sizeof (rbit));
            for (int i = 1; i <= n; ++i) {
                v[i].clear();
                scanf("%d", &w[i]);
                num[cnt++] = w[i]; // 将所有的要进行处理的节点重量以及询问的重量离散化 
            }
            scanf("%d", &m);
            int a, b, c;
            for (int i = 0; i < m; ++i) {
                scanf("%d %d %d", &a, &b, &c);
                ch[a][0] = b, ch[a][1] = c;
            }
            int Q;
            scanf("%d", &Q);
            for (int i = 0; i < Q; ++i) {
                scanf("%d %d", &a, &b);
                v[a].push_back(Node(i, a, b, 0, 0));
                num[cnt++] = b;
            }
            sort(num, num + cnt);
            cnt = unique(num, num + cnt) - num; // 离散化之后一共是cnt个元素 
            for (int i = 0; i < cnt; ++i) {
                mp[num[i]] = i + 1; // 这里加1是为了避免树状数组统计时无法处理0号元素 
            }
            dfs(1); // 题目中约定了1为根
            for (int i = 1; i <= n; ++i) {
                for (int j = 0; j < (int)v[i].size(); ++j) {
                    vv.push_back(v[i][j]);
                }
            }
            sort(vv.begin(), vv.end());
            for (int i = 0; i < (int)vv.size(); ++i) {
                printf(vv[i].ansx == -1 ? "0
    " : "%d %d
    ", vv[i].ansx, vv[i].ansy);
            }
        }
        return 0;
    }
    View Code

    HDU-4606 Occupy Cities

    题意:给定N个城市,现在要从这些城市被外星人攻击还是什么的,要去提前占领这些城市,给出N个城市的二维坐标。同时,在地图上存在一些线段栅栏,一条线路不能够直接越过栅栏。现在有P个士兵,每个士兵可以空降到某一坐城市,士兵到达某座城市后,可以前往另外一座城市,在前往城市的路上需要一些食物消耗,每单位距离对应一个单位食物消耗,没新到一个城市,包裹将被重新填充满。所有士兵都配有一个包裹,包裹单位与食物单位。现在列出一个城市占领的先后序列,要求最多使用P个士兵按照这个序列前去占领,问最少的背包容量为多少?

    分析:首先只要背包容量够大,那么一个士兵也是能够把所有的城市走遍的。这题一个直接的想法就是去二分枚举背包的容量,然后通过几何加之最短路处理将城市与城市之间的最短距离求出来,再然后根据背包容量构造子图,最后求一个有向无环图的最小路径覆盖数,比较最小路径覆盖数与P的关系即可。

    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <cmath>
    using namespace std;
    struct Point {
        double x, y;
        Point() {}
        Point(double _x, double _y) : x(_x), y(_y) {}
        void read() {
            scanf("%lf %lf", &x, &y);
        }
        double operator * (const Point &b) const {
            return x*b.y - y*b.x;
        }
        Point operator - (const Point &b) const {
            return Point(x-b.x, y-b.y);
        }
    };
    
    struct Line {
        Point s, e;
        Line() {}
        Line(Point _s, Point _e) : s(_s), e(_e) {}
    };
    
    const int N = 105;
    const int M = 105;
    const double eps = 1e-6;
    int n, m, p, LIM;
    Point pt[N*3];
    int seq[N];
    double mp[N*3][N*3];
    char G[N][N];
    char vis[N];
    int match[N];
    
    inline int sign(const double &x) {
        return (x < eps) ? -1 : (x > eps);
    }
    
    inline double dist(const Point &a, const Point &b) {
        return sqrt(1.0*(a.x-b.x)*(a.x-b.x) + 1.0*(a.y-b.y)*(a.y-b.y));
    }
    
    void floyd() {
        for (int k = 1; k <= LIM; ++k) {
            for (int i = 1; i <= LIM; ++i) {
                if (i == k) continue;
                for (int j = 1; j <= LIM; ++j)  {
                    if (i == j || j == k) continue;
                    if (sign(mp[i][k] + mp[k][j] - mp[i][j]) < 0) {
                        mp[i][j] = mp[i][k] + mp[k][j];
                    }
                }
            }
        }
    }
    
    bool inter(const Line l1, const Line l2) {
        return
        max(l1.s.x, l1.e.x) > min(l2.s.x, l2.e.x) &&
        max(l2.s.x, l2.e.x) > min(l1.s.x, l1.e.x) &&
        max(l1.s.y, l1.e.y) > min(l2.s.y, l2.e.y) &&
        max(l2.s.y, l2.e.y) > min(l1.s.y, l1.e.y) &&
        sign((l2.s-l1.s)*(l1.e-l1.s)) * sign((l2.e-l1.s)*(l1.e-l1.s)) < 0 &&
        sign((l1.s-l2.s)*(l2.e-l2.s)) * sign((l1.e-l2.s)*(l2.e-l2.s)) < 0;
    }
    
    void build() {
        bool c;
        for (int i = 1; i <= LIM; ++i) {
            for (int j = i+1; j <= LIM; ++j) { // 枚举两个城市之间是否有边相连 
                c = false;
                for (int k = 1, d = 1; k <= m; ++k, d+=2) {
                    if (i > n && k == (i-n+1)/2) continue;
                    if (j > n && k == (j-n+1)/2) continue;
                    if (inter(Line(pt[i], pt[j]), Line(pt[n+d], pt[n+d+1]))) {
                        c = true;
                        break;
                    }
                }
                if (!c) { // 说明没有直线与两个城市之间的连线相交 
                    mp[i][j] = mp[j][i] = dist(pt[i], pt[j]);
                }
            }
        }
        floyd();
    }
    
    bool path(int u) {
        for (int v = 1; v <= n; ++v) {
            if (vis[v] || !G[u][v]) continue;
            vis[v] = 1;
            if (!match[v] || path(match[v])) {
                match[v] = u;
                return true;
            }
        }
        return false;
    }
    
    bool Ac(double mid) {
        memset(G, 0, sizeof (G));
        memset(match, 0, sizeof (match));
        for (int i = 1; i <= n; ++i) { // 有向无环图构造完成 
            for (int j = i+1; j <= n; ++j) {
                if (sign(mid - mp[seq[i]][seq[j]]) >= 0) {
                    G[seq[i]][seq[j]] = 1;
                }
            }
        }
        int cnt = 0;
        for (int i = 1; i <= n; ++i) {
            memset(vis, 0, sizeof (vis));
            if (path(i)) ++cnt;
        }
        return n-cnt <= p;
    }
    
    double bsearch(double l, double r) {
        double mid, ret;
        while (r - l >= eps) {
            mid = (l + r) / 2.0;
            if (Ac(mid)) {
                r = mid - eps;
                ret = mid;
            } else {
                l = mid + eps;
            }
        }
        return ret;
    }
    
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
            scanf("%d %d %d", &n, &m, &p);
            LIM = n+2*m;
            for (int i = 1; i <= LIM; ++i) {
                for (int j = i; j <= LIM; ++j) {
                    mp[i][j] = mp[j][i] = 1e20;
                }
            }
            for (int i = 1; i <= n; ++i) pt[i].read();
            for (int i = 1, j = 1; i <= m; ++i, j+=2) {
                pt[n+j].read(), pt[n+j+1].read();
            }
            for (int i = 1; i <= n; ++i) scanf("%d", &seq[i]);
            build();
            double ret = bsearch(0, 1e5);
            printf("%.2f
    ", ret);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    TSQL编程的全局变量
    一、读大学,究竟读什么?
    受用一生的心理寓言
    字符串函数
    android wait notify实现线程挂起与恢复
    Java Thread.interrupt 中断JAVA线程
    android实现文件下载功能的3种方法
    Android startActivityForResult 和 setResult的使用
    Android 软键盘盖住输入框的问题
    Android蓝牙操作
  • 原文地址:https://www.cnblogs.com/Lyush/p/3212590.html
Copyright © 2020-2023  润新知