zoukankan      html  css  js  c++  java
  • 思维题题集--------一直都很害怕这些题

    https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4991

    给定一个逆波兰表达式,要求添加若干个操作数或者运算符,或者调换任意两个运算符,使得其合法,输出最小步数。

    主要思想:分类讨论。

    ①、当数字大于运算符的时候,可以知道并不需要添加任何数字了,这个时候只考虑交换

    ②、当数字小于运算符的时候,就一定是要添加数字的。

    可以贪心知道,要交换的话,肯定是和最后一个字符交换是最优的。

    样例有

    ***123       ans = 3

    1*1            ans = 1

    1*1*     ans = 1

    #include <bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false)
    using namespace std;
    #define inf (0x3f3f3f3f)
    typedef long long int LL;
    const int maxn = 1e5 + 20;
    char str[maxn];
    vector<int> vc;
    void work() {
        vc.clear();
        scanf("%s", str + 1);
        int lenstr = strlen(str + 1);
        int one = 0, two = 0;
        for (int i = 1; i <= lenstr; ++i) {
            two += str[i] == '*';
            if (str[i] != '0') vc.push_back(i);
        }
        one = lenstr - two;
        int need = two - one + 1;
        int ans = 0, has = 0;
        for (int i = 1; i <= lenstr; ++i) {
            if (str[i] != '*') {
                has++;
            } else {
                if (has >= 2) {
                    has--;
                    continue;
                }
                if (has == 0) {
                    if (need >= 2) {
                        need -= 2;
                        ans += 2;
                        has = 1;
                    } else {
                        int pos = -1;
                        if (vc.size()) {
                            pos = vc.back();
                            vc.pop_back();
                        }
                        swap(str[i], str[pos]);
                        has = 1;
                        ans++;
                    }
                } else {
                    if (need >= 1) {
                        need--;
                        ans++;
                    } else {
                        int pos = -1;
                        if (vc.size()) {
                            pos = vc.back();
                            vc.pop_back();
                        }
                        swap(str[i], str[pos]);
                        has = 2;
                        ans++;
                    }
                }
            }
        }
        cout << ans << endl;
    }
    
    int main() {
    #ifdef local
        freopen("data.txt", "r", stdin);
    //    freopen("data.txt", "w", stdout);
    #endif
        int t;
        scanf("%d", &t);
        while (t--) work();
        return 0;
    }
    View Code

    UESTC - 857 

    https://vjudge.net/contest/171196#problem/H

    给定n个菜,需要使得(重量最小的 / 重量最大的)> T

    询问最小需要切多少刀,保证答案 <= 500

    首先我们可以暴力知道   min / max > T,

    那么每次应该都是切max那个,这样是最优的。因为不可能切min的,这样只会产生一个更小的。使得new_min / max更小

    那么假设不均分地切成了a[1]和a[2](并且a[2] > a[1]不失一般性),则a[1] + a[2] = mx,假设均分地切成了val,则val + val = mx

    假设最小值是mi,则我们目标是取mi / a[2],和mi / val的最大者,可以知道均分切最优。因为val < a[2]

    所以每次都应该均分地切。

    假设每个白菜被切的次数是x[i],那么再切一次这个白菜的时候产新的新值是num[i] / x[i]

    所以维护一个set,维护的是当前白菜的值,然后记录每个白菜被切的次数,一旦需要增加次数,则是重新切的。

    因为比如是4000,切一次会产生2000、2000,切2次是产生1333.33 、 1333.33、 1333.33

    相当于又重新切了。

    #include <bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false)
    using namespace std;
    #define inf (0x3f3f3f3f)
    typedef long long int LL;
    struct Node {
        double pre, now;
        int cnt;
        bool operator < (const struct Node & rhs) const {
            return now < rhs.now;
        }
        Node(double _pre, double _now, int _cnt) {
            pre = _pre, now = _now, cnt = _cnt;
        }
    };
    multiset<Node> ss;
    void work() {
        double t;
        int n;
        cin >> t >> n;
        for (int i = 1; i <= n; ++i) {
            double x;
            cin >> x;
            ss.insert(Node(x, x, 2));
        }
        multiset<Node> :: iterator it1, it2;
        it1 = ss.begin();
        it2 = ss.end();
        it2--;
        int ans = 0;
        while (it1->now / it2->now <= t) {
            int cnt = it2->cnt;
            ss.insert(Node(it2->pre, it2->pre / cnt, cnt + 1));
            ss.erase(it2);
            ans++;
            it1 = ss.begin();
            it2 = ss.end();
            it2--;
        }
        cout << ans << endl;
    }
    
    int main() {
    #ifdef local
        freopen("data.txt", "r", stdin);
    //    freopen("data.txt", "w", stdout);
    #endif
        work();
        return 0;
    }
    View Code

    http://acm.hdu.edu.cn/showproblem.php?pid=5521

    给定m个集合,每个集合中的点相互到达的距离是一样的,都是ti,问点1和点n会面的最短时间,点1和点n同时动。

    思路,直接建边是不可做的,由于集合内的相互两个元素都是满足最短路了,那么我们想另外一个东西代替这种性质。

    每个集合虚拟一个节点new,对于每一个集合的点,我们要做的是x和new连接一条费用为w的边,new和x连接一条费用是0的边

    那么就可以表达出集合内相互最短路的这个特性了,复杂度是O 2n的建边

    然后跑一次最短路即可,好像spfa比dij慢

    #include <bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false)
    using namespace std;
    #define inf (0x3f3f3f3f)
    typedef long long int LL;
    const int maxn = 2e6 + 20;
    struct Edge {
        int u, v, w, tonext;
    }e[maxn * 2];
    int first[maxn], num;
    void addEdge(int u, int v, int w) {
        e[num].u = u, e[num].v = v, e[num].w = w, e[num].tonext = first[u];
        first[u] = num++;
    }
    int tim[maxn], in[maxn];
    LL dis[2][maxn], INF = 1e16;
    bool spfa(int bx, int n, LL dis[]) { //从bx开始,有n个顶点
        for (int i = 1; i <= n; ++i) {
            dis[i] = 1e16;
            tim[i] = 0; //入队次数清0
            in[i] = false; //当前这个节点不在队列里
        }
        queue<int> que;
        while (!que.empty()) que.pop();
        que.push(bx), in[bx] = true, dis[bx] = 0, tim[bx]++;
        while (!que.empty()) {
            int u = que.front();
            if (tim[u] > n) return true; //入队次数超过n次,出现负环
            que.pop();   //in[u] = false ?
            for (int i = first[u]; ~i; i = e[i].tonext) {
                if (dis[e[i].v] > dis[e[i].u] + e[i].w) {
                    dis[e[i].v] = dis[e[i].u] + e[i].w;
                    if (!in[e[i].v]) { //不在队列
                        que.push(e[i].v);
                        in[e[i].v] = true;
                        tim[e[i].v]++;
                    }
                }
            }
            in[u] = false;
        }
        return false;
    }
    int f = 0;
    vector<int> vc;
    struct HeapNode {
        int u;
        LL dis; //dis是到起始点bx的距离
        HeapNode(int from, LL cost) : u(from), dis(cost) {}
        bool operator < (const HeapNode &rhs) const {
            return dis > rhs.dis; //注意,这里的dis小的在前。
        }
    };
    int book[maxn];
    void dij(int bx, LL dis[], int n) {
        memset (book, 0, sizeof book);    // 这些数组只能放在外面,
        for (int i = 1; i <= n; ++i) dis[i] = INF;
        dis[bx] = 0;
        priority_queue<HeapNode> que;
        que.push(HeapNode(bx, dis[bx]));
        while(!que.empty()) {
            HeapNode t = que.top();
            que.pop();
            int u = t.u;     //现在选出的这个u,是dis[]中最小的那个值
            if (book[u]) continue;
            book[u] = true;
            for (int i = first[u]; ~i; i = e[i].tonext) {
                int v = e[i].v;
                if (!book[v] && dis[v] > dis[u] + e[i].w) { //找过的点再用也不行的
                    dis[v] = dis[u] + e[i].w;             //松弛
                    que.push(HeapNode(v, dis[v]));
                }
            }
        }
        return ;
    }
    
    void work() {
        printf("Case #%d:", ++f);
        num = 0;
        memset(first, -1, sizeof first);
        int n, m;
        scanf("%d%d", &n, &m);
        int to = n + 1;
        for (int i = 1; i <= m; ++i) {
            int w, has;
            scanf("%d%d", &w, &has);
            while (has--) {
                int x;
                scanf("%d", &x);
                addEdge(x, to, w);
                addEdge(to, x, 0);
            }
            to++;
        }
        to--;
        dij(1, dis[0], to);
        dij(n, dis[1], to);
        if (dis[0][n] == INF) {
            printf(" Evil John
    ");
            return;
        }
        LL ans = INF;
        for (int i = 1; i <= n; ++i) ans = min(ans, max(dis[0][i], dis[1][i]));
        printf(" %I64d
    ", ans);
        vc.clear();
        for (int i = 1; i <= n; ++i) {
            if (max(dis[0][i], dis[1][i]) == ans) vc.push_back(i);
        }
        sort(vc.begin(), vc.end());
        for (int i = 0; i < vc.size(); ++i) {
            if (i == vc.size() - 1)
                printf("%d
    ", vc[i]);
            else printf("%d ", vc[i]);
        }
    }
    
    int main() {
    #ifdef local
        freopen("data.txt", "r", stdin);
    //    freopen("data.txt", "w", stdout);
    #endif
        int t;
        scanf("%d", &t);
        while (t--) work();
        return 0;
    }
    View Code

    B. Arpa and an exam about geometry

    给定三个点,然后你可以选定一个点,旋转一个任意角度,要求点a转去点b,点b转去点c

    一开始,用正交变化

    然后列公式化简,最后居然解出一个合法解,但是算出来是两个点的。不知道为何,可能联立的时候,我把点消去了。

    但是为什么解出来会有一个合法解,却是不同的两个点。样例二就是这种情况。

    正解应该是:

    ①、所有三角形都有外接圆,圆心在中垂线交点上

    ②、这样的旋转很明显走动了一条弧,在一个圆上动的了,那么转动相同的角度,所走过的弧长是一样长的,因为半径一样。那么如果使得a到达b,b到达c,那么ab == bc要成立

    然后注意的是同一直线上木有圆,不行

    #include <bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false)
    using namespace std;
    #define inf (0x3f3f3f3f)
    typedef long long int LL;
    LL x[3][3];
    LL dis(LL x1, LL y1, LL x2, LL y2) {
        return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
    }
    void work() {
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 2; ++j) {
                scanf("%I64d", &x[i][j]);
            }
        }
        if ((x[1][1] - x[0][1]) * (x[2][0] - x[0][0]) == (x[1][0] - x[0][0]) * (x[2][1] - x[0][1])) {
            printf("No
    ");
            return;
        }
        if (dis(x[1][0], x[1][1], x[0][0], x[0][1]) == dis(x[1][0], x[1][1], x[2][0], x[2][1])) {
            printf("Yes
    ");
        } else printf("No
    ");
    }
    
    int main() {
    #ifdef local
        freopen("data.txt", "r", stdin);
    //    freopen("data.txt", "w", stdout);
    #endif
        work();
        return 0;
    }
    View Code
  • 相关阅读:
    最小生成树——prim
    最短路径——floyd(多源最短路径)
    最短路径——Dijkstra(简易版)
    图的遍历——BFS(队列实现)
    图的遍历——DFS(邻接矩阵)
    图的创建——十字链表
    图的创建——邻接表法
    图的创建——邻接矩阵
    队列——链表实现
    队列——数组实现(循环队列)
  • 原文地址:https://www.cnblogs.com/liuweimingcprogram/p/7384942.html
Copyright © 2011-2022 走看看