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

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

输入一个长度为n的序列a,满足\(1\le a_i\le 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]\)的所有整数即可。由于\(1\le a_[k+1]\le k+1\),而\(sum[k]\ge k\),所以\(sum[k]+p=x+a[k+1]\ (1\le p\le a[k+1],0\le x\le sum[k])\)恒成立。

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

为什么贪心的取是正确的呢?因为\(1\le a_i\le 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;
}
posted @ 2018-03-02 22:53  pechpo  阅读(254)  评论(0编辑  收藏  举报