1. 程式人生 > >清北學堂模擬賽d2t5 吃東西(eat)

清北學堂模擬賽d2t5 吃東西(eat)

cst ios iostream 其中 string bsp 很多 找到 排序

題目描述
一個神秘的村莊裏有4家美食店。這四家店分別有A,B,C,D種不同的美食。LYK想在每一家店都吃其中一種美食。每種美食需要吃的時間可能是不一樣的。
現在給定第1家店A種不同的美食所需要吃的時間a1,a2,…,aA。
給定第2家店B種不同的美食所需要吃的時間b1,b2,…,bB。
以及c和d。
LYK擁有n個時間,問它有幾種吃的方案。

輸入格式(eat.in)
第一行5個數分別表示n,A,B,C,D。
第二行A個數分別表示ai。
第三行B個數分別表示bi。
第四行C個數分別表示ci。
第五行D個數分別表示di。

輸出格式(eat.out)
一個數表示答案。

輸入樣例
11 3 1 1 1
4 5 6
3
2
1

輸出樣例
2

對於30%的數據A,B,C,D<=50
對於另外30%的數據n<=1000。
對於100%的數據1<=n<=100000000,1<=A,B,C,D<=5000,0<=ai,bi,ci,di<=100000000。

分析:這道題剛開始想著用dp,但是這數據明顯不可做啊,狀態都開不下.其實對枚舉優化一下就好了......

試考慮最暴力的做法,四重循環枚舉ABCD.對於四重循環有一個常用的優化就是利用前3個數就能算出第4個數,在這道題中如果我們知道了A+B,就可以知道C+D的具體範圍,那麽把C+D的所有取值排序,找到第一個A+B + C+D>n的C+D的位置i,那麽ans += (i - 1),所以做法就是先枚舉出所有的A+B的組合,再枚舉出所有C+D的組合,排個序,枚舉A+B然後查找C+D的位置累加答案即可.

一個復雜度很高的枚舉,如果我們能夠折半搜索,復雜度就會降低很多,要熟記這些優化.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

int n, A, B, C, D, a[5010], b[5010], c[5010], d[5010],maxn,cur;
int f[100000005], c1[25000010], c2[25000010],cnt1,cnt2;
long long ans = 0;

int main()
{
    scanf(
"%d%d%d%d%d", &n, &A, &B, &C, &D); for (int i = 1; i <= A; i++) scanf("%d", &a[i]); for (int i = 1; i <= B; i++) scanf("%d", &b[i]); for (int i = 1; i <= C; i++) scanf("%d", &c[i]); for (int i = 1; i <= D; i++) scanf("%d", &d[i]); for (int i = 1; i <= A; i++) for (int j = 1; j <= B; j++) if (a[i] + b[j] <= n) { f[a[i] + b[j]]++; maxn = max(maxn, a[i] + b[j]); } for (int i = 0; i <= maxn; i++) while (f[i]) { f[i]--; c1[++cnt1] = i; } maxn = 0; for (int i = 1; i <= C; i++) for (int j = 1; j <= D; j++) if (c[i] + d[j] <= n) { f[c[i] + d[j]]++; maxn = max(maxn, c[i] + d[j]); } for (int i = 0; i <= maxn; i++) while (f[i]) { f[i]--; c2[++cnt2] = i; } for (cur = cnt2; cur >= 1; cur--) if (c1[1] + c2[cur] <= n) break; for (int i = 1; i <= cnt1; i++) { ans += cur; while (cur && c1[i + 1] + c2[cur] > n) cur--; } printf("%lld\n", ans); return 0; }

清北學堂模擬賽d2t5 吃東西(eat)