贪心部分的理论依据:前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]都可以凑出。
实现就是输入时存一下sum,若为奇数就无解,否则再排个序,从大到小扫一遍,选凑成和为sum/2的数的符号为+,其余为-。
先排序,从大到小减(不然会多减小的)
注意sum 用 ll
#include<cstdio> #include<cstdlib> #include<algorithm> #include<iostream> using namespace std; const int maxn=100005; struct node{ int x,id; } a[maxn]; int ans[maxn]; bool cmp(node x,node y) {return x.x>y.x;} int main() { int n; int sum; while (scanf("%d",&n)==1) { long long sum=0; for (int i=1;i<=n;i++) { scanf("%d",&a[i].x); a[i].id=i; sum+=a[i].x; } if (sum%2) { printf("No "); continue; } printf("Yes "); sum/=2; sort(a+1,a+1+n,cmp); for (int i=1;i<=n;i++) { if (a[i].x<=sum) {ans[a[i].id]=1; sum-=a[i].x; } else ans[a[i].id]=-1; } printf("%d",ans[1]); for (int i=2;i<=n;i++) printf(" %d",ans[i]); cout<<endl; } return 0; }