1071: [SCOI2007]组队

本文深入解析了SCOI2007的组队问题,通过dp+单调性的策略,详细阐述了解题思路与算法实现,包括枚举最小值、排序与堆的应用,最终提供两种代码实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1071: [SCOI2007]组队

https://lydsy.com/JudgeOnline/problem.php?id=1071

分析:

  dp+单调性。

  A*(hi–minH)+B*(si–minV)<=C

  Ahi+Bsi<=C+A*minH+B*minV

  如果枚举一个minH,和一个minV的话,那么把数组按Ahi+Bsi排序后,这个数组就具有单调性了。

  这里面的人不一定满足si>=minV和hi>=minH。先固定一个minV,然后把所有大于等于minV的取出来。这样满足了第一个限制。然后枚举minH的时候会由于minH的增加而导致小于了minH,可以把所有加入的放进小根堆里,然后不断弹出不合法的。这样复杂度是$n^2logn$在bzoj上过不了的(luogu上开O2可以过)

  考虑枚举的时候先si>=minV,那么就有A*(hi-minH)<=C+B*minV-B*si,因为要hi>=minH,所0<=左式<=右式,所以C+B*minV-B*si>=0,得到si<=C/B+mv,注意这样还没有满足左式>=0的条件,但是目前si应该满足minV<=si<=C/B+minV。

  这样依然没有满足左式>=0的条件。考虑减去这些不合法的。这里只需要按h从i小到大的扫描所有人,如果这个si是合法的,那么减去。

  这样减是否会减到一些没有枚举过的?

  

就是枚举下面的序列的时候,是否枚举到上面的排列的后面去。

是不会的。

下面序列的满足,hi<=minH,si<=C/B+minV,所以A*hi+B*si最大是A*minH+B*minV+C,刚好到第一个序列的位置。

代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<cctype>
 7 #include<set>
 8 #include<queue>
 9 #include<vector>
10 #include<map>
11 using namespace std;
12 typedef long long LL;
13 
14 inline int read() {
15     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
16     for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
17 }
18 
19 const int N = 100005;
20 
21 struct Node{
22     int h, v; LL tot;
23 }s[N], mv[N], mh[N];
24 
25 bool cmp1(const Node &A, const Node &B) {
26     return A.tot < B.tot; 
27 }
28 bool cmp2(const Node &A, const Node &B) {
29     return A.h < B.h;
30 }
31 bool cmp3(const Node &A, const Node &B) {
32     return A.v < B.v;
33 }
34 
35 int main() {
36     int n = read(); LL A = read(), B = read(), C = read();
37     for (int i = 1; i <= n; ++i) {
38         s[i].h = read(), s[i].v = read(); s[i].tot = A * s[i].h + B * s[i].v;
39         mv[i] = mh[i] = s[i];
40     }
41     sort(s + 1, s + n + 1, cmp1);
42     sort(mh + 1, mh + n + 1, cmp2);
43     sort(mv + 1, mv + n + 1, cmp3);
44     
45     int ans = 0;
46     for (int i = 1; i <= n; ++i) {
47         int p1 = 1, p2 = 1, cnt = 0;
48         LL minv = mv[i].v, limv = minv + C / B;
49         for (int j = 1; j <= n; ++j) {
50             LL minh = mh[j].h, limtot = C + A * minh + B * minv;
51             while (p1 <= n && s[p1].tot <= limtot) {
52                 if (s[p1].v >= minv && s[p1].v <= limv) cnt ++;
53                 p1 ++;
54             }
55             while (p2 <= n && mh[p2].h < minh) {
56                 if (mh[p2].v >= minv && mh[p2].v <= limv) cnt --;
57                 p2 ++;
58             }
59             ans = max(ans, cnt);
60         }
61     }
62     cout << ans;
63     return 0;
64 }
线性
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<cmath>
 6 #include<cctype>
 7 #include<set>
 8 #include<queue>
 9 #include<vector>
10 #include<map>
11 using namespace std;
12 typedef long long LL;
13 
14 inline int read() {
15     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
16     for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
17 }
18 
19 const int N = 100005;
20 
21 struct Node{
22     int h, v; LL tot;
23 }s[N], mv[N], mh[N];
24 
25 bool cmp1(const Node &A, const Node &B) {
26     return A.tot < B.tot; 
27 }
28 bool cmp2(const Node &A, const Node &B) {
29     return A.h < B.h;
30 }
31 bool cmp3(const Node &A, const Node &B) {
32     return A.v < B.v;
33 }
34 priority_queue<int, vector<int>, greater<int> > q; 
35 int main() {
36     int n = read(); LL A = read(), B = read(), C = read();
37     for (int i = 1; i <= n; ++i) {
38         s[i].h = read(), s[i].v = read(); s[i].tot = A * s[i].h + B * s[i].v;
39         mv[i] = mh[i] = s[i];
40     }
41     sort(s + 1, s + n + 1, cmp1);
42     sort(mh + 1, mh + n + 1, cmp2);
43     sort(mv + 1, mv + n + 1, cmp3);
44     
45     int ans = 0;
46     for (int i = 1; i <= n; ++i) {
47         int p = 1, cnt = 0;
48         LL minv = mv[i].v;
49         for (int j = 1; j <= n; ++j) {
50             LL minh = mh[j].h, limtot = C + A * minh + B * minv;
51             while (p <= n && s[p].tot <= limtot) {
52                 if (s[p].v >= minv && s[p].h >= minh) cnt ++, q.push(s[p].h);
53                 p ++;
54             }
55             while (!q.empty() && q.top() < minh) cnt--, q.pop();
56             ans = max(ans, cnt);
57         }
58     }
59     cout << ans;
60     return 0;
61 }

 

转载于:https://www.cnblogs.com/mjtcn/p/10036778.html

中描述了一个幼儿园里分配糖果的问题,每个小朋友都有自己的要求。问题的输入包括两个整数NN和KK,表示幼儿园里的小朋友数量和要满足的要求数量。接下来的KK行表示小朋友们的要求,每行有三个数字,XX,AA,BB。如果X=1,表示第AA个小朋友分到的糖果必须和第BB个小朋友分到的糖果一样多;如果X=2,表示第AA个小朋友分到的糖果必须少于第BB个小朋友分到的糖果;如果X=3,表示第AA个小朋友分到的糖果必须不少于第BB个小朋友分到的糖果;如果X=4,表示第AA个小朋友分到的糖果必须多于第BB个小朋友分到的糖果;如果X=5,表示第AA个小朋友分到的糖果必须不多于第BB个小朋友分到的糖果。这个问题可以被看作是一个差分约束系统的问题。 具体地说,可以使用差分约束系统来解决这个问题。差分约束系统是一种通过给变量之间的关系添加约束来求解最优解的方法。对于这个问题,我们需要根据小朋友们的要求建立约束条件,并通过解决这个约束系统来得出最小的糖果数量。 在问题的输入中,X的取值范围为1到5,分别对应不同的关系约束。根据这些约束,我们可以构建一个差分约束图。图中的节点表示小朋友,边表示糖果数量的关系。根据不同的X值,我们可以添加相应的边和权重。然后,我们可以使用SPFA算法(Shortest Path Faster Algorithm)来求解这个差分约束系统,找到满足所有约束的最小糖果数量。 需要注意的是,在读取输入时需要判断X和Y是否合法,即是否满足X≠Y。如果X=Y,则直接输出-1,因为这种情况下无法满足约束条件。 综上所述,为了满足每个小朋友的要求,并且满足所有的约束条件,我们可以使用差分约束系统和SPFA算法来求解这个问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【差分约束系统】【SCOI2011】糖果 candy](https://blog.youkuaiyun.com/jiangzh7/article/details/8872699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [P3275 [SCOI2011]糖果(差分约束板子)](https://blog.youkuaiyun.com/qq_40619297/article/details/88678605)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值