• 2019-08-02 纪中NOIP模拟B组


    T1 [JZOJ1420] 佳肴

    题目描述

      佳肴就是非常美味的菜的意思,佳肴最关键的是选择好原料。

      现在有N种原料,每种原料都有酸度S和苦度B两个属性,当选择多种原料时,总酸度为每种原料的酸度之积,总苦度为每种原料的苦度之和。

      正如大家所知,佳肴是既不酸也不苦的,因为要保证所选的原料使得总酸度和总苦度差的绝对值最小。

      由于佳肴不能只有水,所以必须至少选择一种佳肴。

    分析

      签到题 无脑暴搜

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 15
    
    int n, ans = inf;
    int a[N], b[N];
    
    void dfs(int now, int mul, int sum) {
        ans = min(ans, abs(mul - sum));
        for (int i = now + 1; i <= n; i++)
            dfs(i, mul * a[i], sum + b[i]);
    }
    
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d%d", a + i, b + i);
        for (int i = 1; i <= n; i++)
            dfs(i, a[i], b[i]);
        printf("%d
    ", ans);
        
        return 0;
    }
    View Code

    T2 [JZOJ1308] 取数游戏

    题目描述

      Alice想让Bob陪他去看《唐山大地震》,但由于Bob是个很感性的人,怕流泪不想去,但又不好意思以这个作为拒绝的理由,便提出玩一个游戏。

      N个正整数围成一圈,规则如下:

      • 两个玩家轮流取数;

      • 最开始先手的玩家可以取任意一个数x;

      • 从第二步开始当前玩家只能取x(上一玩家刚刚取的数)左右两边相邻的数;

      • 直到取完所有的数,游戏结束;

      • 取得较多奇数的玩家获胜。

      Bob为了显示大度,让Alice先取,但他忘了自己和Alice都是绝顶聪明之人,现在Alice请你帮他计算第一步有多少种取法使得最终获得胜利。

    分析

      由于是环形的情况,我们可以将序列复制一遍,通过枚举第一次选取的数,把这个位置断开,就变成了区间DP

      设 $g[i]$ 表示第 $i$ 位的奇偶(奇数为 $1$,偶数为 $0$),$f[i][j]$ 表示从区间 $[i,j]$ 开始先手与后手的最大奇数个数差

      于是可以得到 $f[i][j]=max(g[i]-f[i+1][j],g[j]-f[i][j-1])$

      最后枚举第一次选取数字的所有情况,答案就是其中 $f>0$ 的数量

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 105
    
    int n, ans;
    int g[2 * N], f[2 * N][2 * N];
    
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", g + i);
            g[n + i] = (g[i] %= 2);
            if (g[i]) f[i][i] = f[n + i][n + i] = 1;
        }
        for (int l = 2; l < n; l++)
            for (int i = 1; i + l - 1 < 2 * n; i++) {
                int j = i + l - 1;
                f[i][j] = max(g[i] - f[i + 1][j], g[j] - f[i][j - 1]);
            }
        for (int i = 1; i <= n; i++) {
            int j = i + n - 1;
            f[i][j] = g[i] - f[i + 1][j];
            if (f[i][j] > 0) ans++;
        }
        printf("%d
    ", ans);
        
        return 0;
    }
    View Code

    T3 [JZOJ1381] 删除

    题目描述

      Alice上化学课时又分心了,他首先画了一个3行N列的表格,然后把数字1到N填入表格的第一行,保证每个数只出现一次,另外两行他也填入数字1到N,但不限制每个数字的出现次数。

      Alice现在想删除若干列使得每一行排完序后完全一样,编程计算最少需要删除多少列。

    分析

      由于删除后每一行排完序完全相同,所以其中任意数字必须在三行中均出现

      于是找出第二行或第三行中未出现的数,删除该数在第一行中所在的一整列

      然后反复查找和删除,直到不存在某一列需要被删除,被删除的列数即为答案

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 100005
    
    int n, ans;
    int a[N], b[N], c[N];
    int pos[N], book[N], book1[N], book2[N];
    
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", a + i);
            book[a[i]] = 1;
            pos[a[i]] = i;
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d", b + i);
            book1[b[i]]++;
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d", c + i);
            book2[c[i]]++;
        }
        while (1) {
            int ok = 1;
            for (int i = 1; i <= n; i++)
                if (!book1[i] && book[i]) {
                    ans++;
                    book[i] = 0;
                    book1[b[pos[i]]]--;
                    book2[c[pos[i]]]--;
                    ok = 0;
                }
            for (int i = 1; i <= n; i++)
                if (!book2[i] && book[i]) {
                    ans++;
                    book[i] = 0;
                    book1[b[pos[i]]]--;
                    book2[c[pos[i]]]--;
                    ok = 0;
                }
            if (ok) break;
        }
        printf("%d
    ", ans);
        
        return 0;
    }
    View Code

    T4 [JZOJ1382] 区间

    题目描述

      Alice收到一些很特别的生日礼物:区间。即使很无聊,Alice还是能想出关于区间的很多游戏,其中一个是,Alice从中选出最长的不同区间的序列,其中满足每个区间必须在礼物中,另序列中每个区间必须包含下一个区间。

      编程计算最长序列的长度。

    分析

      先将所有区间以左端点为第一关键字从小到大排序,以右端点为第二关键字从大到小排序

      首先这样排序的好处是在判断区间的包含关系时不需要考虑左端点

      设区间 $s$ 的左端点为 $l$,右端点为 $r$,则有

      对于任意 $i<j$, 若 $s[i].r geq s[j].r$,则 $s[i]$ 包含 $s[j]$

      此时问题就变成了,求区间右端点的最长不上升子序列

      根据数据规模,求最长不上升子序列时需要二分查找

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 100005
    
    int n, ans;
    int f[N];
    
    struct Stv {
        int l, r;
    } s[N];
    
    bool cmp(Stv a, Stv b) {
        if (a.l != b.l) return a.l < b.l;
        return a.r > b.r;
    }
    
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d%d", &s[i].l, &s[i].r);
        sort(s + 1, s + n + 1, cmp);
        for (int i = 1; i <= n; i++) {
            int l = 1, r = i;
            while (l <= r) {
                int mid = (l + r) >> 1;
                if (f[mid] >= s[i].r) l = mid + 1;
                else r = mid - 1;
            }
            f[l] = s[i].r;
            ans = max(ans, l);
        }
        printf("%d
    ", ans);
        
        return 0;
    }
    View Code
  • 相关阅读:
    迈向架构设计师之路系列—简单对象访问模式(一)
    C#温故而知新学习系列之.NET运行机制—.NET中托管代码是指什么?(三)
    C#温故而知新学习系列之面向对象编程—静态方法(九)
    Android深入浅出系列之Android开发环境搭建—JDK(一)
    C#温故而知新学习系列之面向对象编程—自动属性(十一)
    C#温故而知新学习系列之面向对象编程—方法的重载(八)
    C#温故而知新学习系列之面向对象编程—分布类是什么?(十四)
    C#温故而知新学习系列之面向对象编程—属性(十二)
    C#温故而知新学习系列之面向对象编程—构造函数(七)
    C#温故而知新学习系列之.NET运行机制—.NET Framework概述及其组成(一)
  • 原文地址:https://www.cnblogs.com/Pedesis/p/11289429.html
Copyright © 2020-2023  润新知