这道题是一道好题,我想了很久都没有想出合适的方案,这道题考了我们贪心(不确定)+数学推导(确定)的能力,看来我的数学逻辑以及推理能力还需要加强啊。
题意不说,直接上思路:
由于1<=ai<=i的条件,我们需要从这里入手求解,首先,我们需要证明什么时候优解,什么时候没解,解决这些再讨论怎么解的问题。
结论:对于在1 <= a[i] <= i时, 前i个数一定可以凑出1~sum[i]的所有整数
证明:数学归纳,n=1时成立,假设n=k之前所有项都成立,当n=k+1时。sum[k+1]=sum[k]+a[k+1]。
只需证明能凑出sum[k]+1~sum[k+1]间的整数即可。设1≤p≤a[k+1],sum[k]+p=sum[k]+a[k+1]-(a[k+1]-p)。
因为1≤a[i]≤i,易得sum[k]≥k,a[k+1]-p≤k。又因为已知前k个数可以凑出1~sum[k],所以一定可以凑出a[k+1]-p。
所以只需从之前凑出sum[k]里面剪掉凑出a[k+1]-p的数就可以凑出sum[k]+p。所以从1~sum[k+1]都可以凑出。
https://www.cnblogs.com/zyb993963526/p/6357777.html
这个是从别人的博客上面找到的,这个大家自行理解,我就不说了。
然后是如何求解的问题,下面是解决思路:
我们把sum除2,然后就可以随便选了啊(从后往前选),只要a[i]的值小于当前sum值,我们就选了,赋值成1;否则赋值为-1;
我看有的题解要排序,其实不需要,排序无非是从大到小取...我反正觉得意义不大...
https://www.cnblogs.com/fzfn5049/p/7825185.html
这个大家看一看,我看到这个部分时,会有疑惑,为什么这样能够求出解呢?到底需不需要排序呢?????
经过思考后,我明白了这样解的正确性,并且知道不需要排序,为什么?
下面是证明:
当sum < a[i]时,我们不能够用sum = sum - a[i],目的也很简单(我们不能够得到负数,如果是负数,就不能保证得到的正负数的绝对值恰好是一半了),
那么我们需要确定i之前(即1 - i-1)是否满足它们的和 >= sum,由于a[i] <= i, a[1]+a[2]+...+a[i-1]>=i-1, 如果sum<a[i]那么sum<i所以我们发现a[1]+a[2]+...+a[i-1]>=sum,
类比最上面的结论(数学归纳的那部分)我们可以得到1 - i-1这部分一定能够组成sum,所以放心的继续向下查找。
当sum>=a[i]时,我们让sum = sum-a[i],其实证明与sum<a[i]的那部分类似,在-a[i]之前sum一定<=a[1]+a[2]+...+a[i-1](想一想,为什么),所以两边同时减去a[i],
自然也能够保证1 - i-1这部分一定能够组成sum-a[i],所以减完以后也放心的向下查找。
下面是实现:
// UVa 1614 // 数学证明(贪心) #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 100000 + 10; int n, a[maxn], ans[maxn]; int main() { while (scanf("%d", &n) == 1) { long long sum = 0; for (int i = 0; i < n; ++i) { scanf("%d", &a[i]); sum += a[i]; } if (sum&1) { printf("No "); } else { printf("Yes "); memset(ans, 0, sizeof(ans)); sum >>= 1; for (int i = n-1; i >= 0; --i) if (sum >= a[i]) { sum -= a[i]; ans[i] = 1; } for (int i = 0; i < n; ++i) { if (ans[i] == 0) printf("1 "); else printf("-1 "); } printf(" "); } } return 0; }