uva 1614奇怪的股市(归纳法证明,贪心)

uva 1614奇怪的股市(归纳法证明,贪心)

输入一个长度为n的序列a,满足(1le a_ile i),要求确定每个数的正负号,使得所有数的总和为0.例如a={1, 2, 3, 4},则4个数的符号分别是1, -1, -1, 1即可。但若a={1, 2, 3, 3},则无解。n<=1e5。

这道题相当于要找到两堆相等的数。若序列中数的总和为奇数,那么拆出来的两堆数无论如何都不可能相等,所以无解。由于这道题的特殊性质,可用归纳法证明总和为偶数时一定有解。

现在要证明,用前i个数的全部或部分可以凑出0到sum[i]的所有整数。n=1时这是成立的。假设n=k之前所有项都成立,那么(sum[k+1]=sum[k]+a[k+1])。只需证明能凑出(sum[k]+1)(sum[k]+a[k+1])的所有整数即可。由于(1le a_[k+1]le k+1),而(sum[k]ge k),所以(sum[k]+p=x+a[k+1] (1le ple a[k+1],0le xle sum[k]))恒成立。

这样一来,就证明了用前n个数,可以凑出sum[n]/2。当sum[n]是偶数时,另一半也就是sum[n]/2。现在的问题就是找出和等于sum[n]/2的数。直接将a数组从大到小排序,然后贪心的取即可。

为什么贪心的取是正确的呢?因为(1le a_ile i),排序完的数组必定也满足这个条件。所以只要贪心的取显然有解。

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn=1e5+5;
struct node{
    int data, id;
}a[maxn];
int n, chose[maxn];
long long sum;

bool cmp1(node &x, node &y){
   return x.data>y.data; }

//100万才需要读优
int main(){
    while (~scanf("%d", &n)){
        sum=0;
        for (int i=0; i<n; ++i){
            scanf("%d", &a[i].data);
            sum+=a[i].data; a[i].id=i;
        }
        if (sum&1){
            puts("No"); continue; }
        else sum>>=1;
        sort(a, a+n, cmp1);
        for (int i=0; i<n; ++i)
            if (a[i].data<=sum){
                sum-=a[i].data;
                chose[a[i].id]=1;
            } else chose[a[i].id]=-1;
        puts("Yes");
        for (int i=0; i<n; ++i)
            printf("%d ", chose[i]);
        puts("");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/MyNameIsPc/p/8495362.html