Northwestern European Regional Contest 2017-I题- Installing Apps题解

一、题意

有一个手机,容量为$C$,网上有$N$个app,每个app有个安装包大小$d_i$,有个安装后的占用空间大小$s_i$,安装app是瞬间完成的,即app的占用空间可以瞬间由$d_i$变成$s_i$,而不需要其他多余的空间。问这个手机最多可以安装多少个app,输出最多可以安装的app数量和安装顺序。

二、思路

很显然的$dp$。按照$max(d,s)-s$从大到小排序。$dp[i][j]$表示在前$i$个app中,占用空间不超过$j$的条件下最多可以安装的app的数量。那么,有如下递推式:

枚举$1 \le i \le N,0 \le j \le C$,如果$j<s_i$,$dp[i][j]=dp[i-1][j]$;

如果$j \ge s_i且C-(j-s_i) \ge d_i$,$dp[i][j]=max(dp[i-1][j],dp[i-1][j-s_i]+1)$;

初始状态,全部的$dp$都是$0$。

然后,在状态转移的时候,需要记录选择的路径。

三、注意点

1、“如果$j \ge s_i且C-(j-s_i) \ge d_i$”,意思是,如果$j \ge s_i$且选择当前这个app之前剩余空间大于当前这个app的安装包大小,那么就可以安装这个app。

2、最后的答案不一定是$dp[N][C]$,而是$max\{dp[N][j]|0 \le j \le C\}$。

3、这题其实就是01背包模型,记录路径的方案和01背包一样。

4、切记:记录路径时,用额外数组的方式最靠谱。

四、代码

#include<bits/stdc++.h>
using namespace std;
struct app {
    int d, s, id;
} p[510];
bool cmp(app a1, app a2) {
    return max(a1.d, a1.s) - a1.s > max(a2.d, a2.s) - a2.s;
}
int N, C, dp[510][10010], ans[510], acnt;
bool path[510][10010];
int main() {
    scanf("%d%d", &N, &C);
    for(int i = 1; i <= N; ++i)scanf("%d%d", &p[i].d, &p[i].s), p[i].id = i;
    for(int i = 0; i <= N; ++i) {
        for(int j = 0; j <= C; ++j)dp[i][j] = 0;
    }
    sort(p + 1, p + N + 1, cmp);
    for(int i = 1; i <= N; ++i) {
        for(int j = 0; j <= C; ++j) {
            dp[i][j] = dp[i - 1][j];
            if(j >= p[i].s) {
                int last = j - p[i].s;
                if(C - last >= p[i].d) {
                    if(dp[i][j] < dp[i - 1][last] + 1) {
                        dp[i][j] = dp[i - 1][last] + 1;
                        path[i][j] = 1;
                    }
                }
            }
        }
    }
    int V, aa = 0;
    for(int j = C; j >= 0; --j) {
        if(aa < dp[N][j]) {
            aa = dp[N][j], V = j;
        }
    }
    for(int i = N, j = V; i > 0; --i) {
        if(path[i][j]) {
            ans[++acnt] = p[i].id;
            j -= p[i].s;
        }
    }
    reverse(ans + 1, ans + acnt + 1);
    printf("%d\n", acnt);
    for(int i = 1; i <= acnt; ++i)printf("%d ", ans[i]);
    return 0;
}
/*
3 4
2 1
3 2
3 3
*/

原文地址:https://www.cnblogs.com/565261641-fzh/p/9651977.html

时间: 2024-10-08 11:30:42

Northwestern European Regional Contest 2017-I题- Installing Apps题解的相关文章

2017-2018 Northwestern European Regional Contest (NWERC 2017)

Rank Solved A B C D E F G H I J K --/-- 6/11 . O . O . . O O O . O O: 当场通过 ?: 赛后通过 .: 尚未通过 A Ascending Photo unsolved B Boss Battle solved by chelly chelly's solution C Connect the Dots unsolved D Dunglish solved by ABerror ABerror's solution E Engli

2017-2018 ACM-ICPC Northern Eurasia (Northeastern European Regional) Contest (NEERC 17)

2017-2018 ACM-ICPC Northern Eurasia (Northeastern European Regional) Contest (NEERC 17) A 题意:有 n 个时刻,第 i 个时刻要么会在 (xi,yi) 生成一个半径为 yi 的圆,要么射击 (xi,yi) 这个点,如果该点在某个圆内则把对应圆删除并输出该圆的标号,否则输出 -1 .任意时刻圆之间不会相交(可以相切). \(n \le 2*10^5, -10^9 \le x_i,y_i \le 10^9, y

2016-2017 ACM-ICPC Northwestern European Regional Programming Contest (NWERC 2016) 个人题解

Problem C Careful Ascent 可怜的我们被卡在了签到题上,当时用的二分来做,结果速度的左右区间写成了[0,1e32],而改成[-1e32,1e32]就通过了,哎-,怎么就没想到去改一下区间的范围呢. 下面是正常的数学解法,类似于解一元一次方程. #include <iostream> using namespace std; typedef long long ll; ll x,y,n; ll a,b;double c; int main(){ cin>>x&g

2018-2019 ICPC Northwestern European Regional Programming Contest (NWERC 2018)

J题队友犯了初始化的错,白给6发,本来能1A的 B: solver:lzh.czq 就是个拓扑排序 1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ff first 4 #define ss second 5 #define mp make_pair 6 typedef long long ll; 7 typedef pair<int, int> pii; 8 9 vector<int> g[400

2016-2017 ACM-ICPC Northeastern European Regional Contest (NEERC 16)

D:上下界费用流 将每个点和每个长度D的区间看作边,限制条件看作流量上下界,差分建图,无源汇最大费用费用流,非常巧妙的使用了差分建图. #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define N 100003 #define inf 2000000000 #defi

ACM-ICPC SouthWestern Europe Regional Contest 2017题解

题目地址 http://codeforces.com/gym/101635/ A 题: 计算两个数组元素之间最有可能的差值,注意数据全部非法时的情况 #include<bits/stdc++.h> using namespace std; const int maxn = 2005; int a[maxn], b[maxn]; int main() { int n, m; scanf("%d%d", &n, &m); unordered_map<int

2015-2016 ACM-ICPC Northeastern European Regional Contest (NEERC 15)C - Cactus Jubilee

题意:给一颗仙人掌,要求移动一条边,不能放在原处,移动之后还是一颗仙人掌的方案数(仙人掌:无向图,每条边只在一个环中),等价于先删除一条边,然后加一条边 题解:对于一颗仙人掌,分成两种边,1:环边:环上的边2,树边:非环上的边 考虑1.删除树边,那么只需联通两个联通快,那么方案数就是两个联通块乘积-1(除去删除的边) 2.删除环边,那么我们假设删除所有环,那么图变成了深林,方案数就是深林中每棵树任意两点连接,方案数就是全部的和,先维护一个每个环上的点有多少树边,对于每个树边联通块(大小x)共贡献

Asia Yokohama Regional Contest 2018 B题 - Arithmetic Progressions(dp)

https://codeforces.com/gym/102082 题意 给定一些数,可以重新排序,求其中最长的等差数列的长度 思路 d[i][j]表示以a[i]和a[j]为开头的等差数列的最大长度,具体见代码 1 #define bug(x,y) cout<<"i="<<x<<": "<<y<<endl 2 #define IO std::ios::sync_with_stdio(0); 3 #inclu

Asia Yokohama Regional Contest 2018 G题 What Goes Up Must Come Down(树状数组求逆序对)

https://codeforces.com/gym/102082 题意: 给一个数组大小不超过1e5,每个数的值也是1e5以内,可以交换相邻两个数,求保证它呈现一个非递减再非递增的趋势的最小交换次数. 题解: 对每个数来说,只有两种情况,要么参与非递减部分要么参与非递增部分,对于前者它要移的次数就是在它之前与他构成的逆序对数,对于后者它要移的次数就是在它之后与他构成的逆序对数,那我们取较小的加入到答案就做完了. #define bug(x,y) cout<<"i="<