题目大意:给你n个得分值,让你最多投四次飞镖,问可以扎到的分数值之和小于m的最大值是多少(n<=1000)
刚开始看到这道题以为是个背包看到数据范围m的值就知道背不动了...后来仔细想了一下,对于最多拿四个数来
讲,答案可能的来源有拿1个(1000种)、拿2个(1000^2种),拿3个(1000^3), 以及拿4个(1000^4种)。这
样直接枚举所有答案来源的话百分之九十九会超时。但是再想一下,对于拿3个来说,它必然可以转换成一种拿1个
+拿2个的和的状态,同理,拿4个可以转换成拿2个之和的状态。这样我们就可以暴力枚举出拿1个和拿2个的所有答
案数(10^6),然后对于这些状态可以稳定得到拿3个或者拿4个的状态。假如数组sum[]表示拿1个或2个的答案,那
么对于sum[i]来说,由它组成的3个或者4个的不小于m的最大值,sum[i]+sum[x]<m,这样只需要二分得到最大的x即可,
当然之前要对sum[]进行排序预处理,总的时间复杂度O(10^6*log(10^6)) ≈ 10^7。尝试写了一发果然过了。这道题一
开始没思路连题解都找不到,以后不能再懒了,多写博客,帮人帮己,学习使我快乐。
#include <stdio.h> #include <algorithm> #include <string.h> #include <queue> using namespace std; int nums[1100], sum[1100000]; int erfen(int i, int cnt, int m) { int l = 1, r = cnt-1, Index = 0; while(l<=r) { int mid = (l+r)/2; if(sum[i]+sum[mid]<m) { Index = mid; l = mid+1; } else r = mid-1; } return Index; } int main() { int n, m; while(scanf("%d %d", &n, &m), n||m) { for(int i=1; i<=n; i++) scanf("%d", &nums[i]); int cnt = 1; for(int i=1; i<=n; i++)///取一个或两个的所有可得到答案 { sum[cnt++] = nums[i];///取一个 for(int j=1; j<=n; j++) sum[cnt++] = nums[i]+nums[j];///取两个 } sort(sum+1, sum+cnt); cnt = unique(sum+1, sum+cnt)-sum; int ans = 0; for(int i=1; i<cnt; i++) { if(sum[i]>=m) break; ans = max(ans, sum[i]); int Index = erfen(i, cnt, m); ans = max(ans, sum[i]+sum[Index]); } printf("%d\n", ans); } return 0; }View Code