思维题题集--------一直都很害怕这些题

 

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\n");
        return;
    }
    LL ans = INF;
    for (int i = 1; i <= n; ++i) ans = min(ans, max(dis[0][i], dis[1][i]));
    printf(" %I64d\n", 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\n", 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\n");
        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\n");
    } else printf("No\n");
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}
View Code

 

posted on 2017-08-17 23:11  stupid_one  阅读(251)  评论(0编辑  收藏  举报

导航