• Cloakroom


    Cloakroom

    题目描述

    • 有n件物品,每件物品有三个属性 a[i],b[i],c[i] (a[i]<b[i])。
    • 个询问,每个询问由非负整数 m,k,s 组成,问是否能够选出某些物品使得:
      1. 对于每个选的物品 i,满足 a[i]<=m 且 b[i]>m+s。
      2. 所有选出物品的 c[i]的和正好是 k。

    输入格式

    • 第一行一个正整数 n(n<=1,000),接下来 n 行每行三个正整数,分别表示 c[i],a[i],b[i] (c[i]<=1,000,1<=a[i]<b[i]<=109)。
    • 下面一行一个正整数 q(q<=1,000,000),接下来 q 行每行三个非负整数 m,k,s(1<=m<=(10^9),1<=k<=100,000,0<=s<=(10^9))。

    输出格式

    • 输出 q行,每行为 "TAK "(yes)或"NIE"(no),第 i 行对应第 i 次询问的答案。

    样例输入

    5
    6 2 7
    5 4 9
    1 2 4
    2 5 8
    1 3 9
    5
    2 7 1
    2 7 2
    3 2 0
    5 7 2
    4 1 5
    

    样例输出

    TAK
    NIE
    TAK
    TAK
    NIE
    

    Solve

    看这道题的题解都写的有些简单,特此补上一篇稍详细一些的题解(代码有注释)。

    • 题目大意

    n件物品分别有属性a,b,c.

    p个询问分别有属性m,k,s.

    对于每个询问给出是否存在满足以下三个条件的情况,有输出TAK,否则输出NIE

    1. 每个物品(a leq m)
    2. 每个物品(b>m+s)
    3. 所有物品c的和等于k
    • 解题思路

    • 考虑暴力,枚举k的所有组成情况并进行记录,每次询问仅须判断c的和为k的几个数,这样预处理都有(2^{1000}),妥妥49分TLE。

    • q[k][i].a表示和为k的第i种方案中所有物品a属性的最大值。
      q[k][i].b表示和为k的第i种方案中所有物品b属性的最小值。

    //...
    struct Node {
    	int a, b;
    	Node() {}
    	Node(int x, int y) {
    		a = x; b = y;
    	}
    };
    //...
    vector<Node> q[100005];//用q数组记录,便于查询
    void Dfs(int x, int big, int small, int sum) {
        if (big > small || sum > 100000) return;
        //剪枝优化,a比b大或总和超过数据范围就不再考虑
        q[sum].push_back(Node(big, small));
        for (int i = x + 1; i <= n; ++i)
            Dfs(i, max(big, a[i]), min(small, b[i]), sum + c[i]);
    }
    int main() {
        //...
        while (Q--) {
            int m, k, s, f = 0;
            scanf("%d%d%d", &m, &k, &s);
            for (int i = 0; i < q[k].size(); ++i)//枚举每种方案进行判断
                if (q[k][i].a <= m && q[k][i].b > m + s) f = 1;
            puts(f ? "TAK" : "NIE");
        }
        return 0;
    }
    //错误解法只列出关键部分,提供暴力思路
    
    • 这么大的数据,而且有3个条件需要满足,考虑离线算法。
    1. 看第一个条件:每个物品(a leq m)
      将物品按照a的大小排序,询问按照m的大小排序,这样,对于第一个条件满足当前询问的物品,也一定会满足后面的询问的第一个条件,节省了一些时间。

    2. 每个物品(b>m+s),先略过

    3. 所有物品c的和等于k
      类似于背包问题,需要把背包装满,可以按照背包的思路进行dp。

    • f[k]表示,在满足(a leq m)的物品中c属性之和为k的方案中最小的 b 属性的最大值

      • 这一点需要重点理解,需要满足每个物品(b>m+s),就需要最小的b比m+s大就可以,但是c属性的和为k的方案数有可能不止一种,需要找到最优的就是在满足x是这个方案中最小的b属性值的前提下尽可能的找x最大的方案。越说越迷糊,看代码可能会好懂一些。

    Code

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    struct Node1 {
        int a, b, c;
        bool operator < (const Node1 &b) const {
            return a < b.a;
        }//重载运算符,对物品按a值从小到大排序
    }a[1005];
    struct Node2 {
        int m, k, s, id;
        bool operator < (const Node2 &b) const {
            return m < b.m;
        }/重载运算符,对询问按m值从小到大排序
    }b[1000005];
    int n, q, f[100005];//f数组题解中的加粗部分进行了详细的解释
    bool ans[1000005];
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            scanf("%d%d%d", &a[i].c, &a[i].a, &a[i].b);
        scanf("%d", &q);
        for (int i = 1; i <= q; ++i)
            scanf("%d%d%d", &b[i].m, &b[i].k, &b[i].s), b[i].id = i;
           //记录编号id,离线排序后便于保存答案
        sort(a + 1, a + n + 1);//对物品按a值从小到大排序
        sort(b + 1, b + q + 1);//对询问按m值从小到大排序
        f[0] = 1 << 30;//初始化f[0]为极大值,防止在运行 min(f[k-a[j].c], a[j].b)时出现结果都是0的情况
        for (int i = 1, j = 1; i <= q; ++i) {
            for (; j <= n && a[j].a <= b[i].m; ++j)//满足条件1:a<=m
                for (int k = 100000; k >= a[j].c; --k)
                    f[k] = max(f[k], min(f[k-a[j].c], a[j].b));
            if (f[b[i].k] > b[i].m + b[i].s) ans[b[i].id] = 1;
            //满足条件3:c之和==k
            //满足条件2:b>m+s
        }
        for (int i = 1; i <= q; ++i)
            puts(ans[i] ? "TAK" : "NIE");
        //三目运算符,个人比较喜欢使用,挺方便
        return 0;
    }
    
  • 相关阅读:
    UML中常用的几种图
    JVM调优问题与总结
    可视化算法学习网站
    [MacOS]查看端口占用进程
    [MacOS]停止"访达"操作,然后再次尝试推出磁盘
    [MacOS]蓝牙重置
    [CentOS7]扩充swap空间
    [5500V5]开启snmpv2
    [Cisco]MDS 9148S 开启snmp v2
    [CentOS7]测试udp端口
  • 原文地址:https://www.cnblogs.com/shawk/p/13337189.html
Copyright © 2020-2023  润新知