n很大,不能DP,令我不得不想到了贪心,结果贪心完了莫名其妙的AC。。
之后查找该题的证明,发现网上题解都不求甚解,只有一个博客给出了一个说法: 因为 1 <= a[i] <= i,可用数学归纳法证明,用 a[1]~a[i] 的部分或全部元素通过求和,一定可以凑出 1~sum[i] 的每个整数(sum[i]是前 i 个元素的和)
的确,1 <= a[i] <= i是个奇怪的限制条件,大家不妨记住这个奇妙的结论, 如果我以后得到证明方法会再回来补充 。
有了这个结论, 我们就可以贪心的寻找答案了,从大到小排序,如果加上当前值cur += a[k]超过了sum/2 ,那么说明这个值不对,而且这个值比sum/2 - cur大,那么按照我们上面的结论,大可以不要这个数, 因为剩下的数一定可以凑出sum/2 - cur 。
细节参见代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 10;
int n,vis[maxn];
struct point{
int id,v,ans;
}a[maxn];
bool cmp1(point a,point b) {
return a.v > b.v;
}
bool cmp2(point a, point b) {
return a.id < b.id;
}
int main() {
while(~scanf("%d",&n)) {
ll sum = 0;
for(int i=0;i<n;i++) {
scanf("%d",&a[i].v);
a[i].id = i;
sum += a[i].v;
}
bool ok=false ;
if(sum%2 == 0) ok = true;
if(ok) {
ok = false;
sort(a,a+n,cmp1);
ll cur = 0;
int i;
for(i=0;i<n;i++) {
cur += a[i].v;
if(cur == sum/2) { a[i].ans = 1; ok = true; break; }
else if(cur > sum/2) { cur-=a[i].v; a[i].ans = -1; }
else a[i].ans = 1;
}
for(i=i+1;i<n;i++) a[i].ans = -1;
}
if(ok) {
printf("Yes\n");
sort(a,a+n,cmp2);
printf("%d",a[0].ans);
for(int i=1;i<n;i++) printf(" %d",a[i].ans);
printf("\n");
}
else printf("No\n");
}
return 0;
}