UVa1614

这道题是一道好题,我想了很久都没有想出合适的方案,这道题考了我们贪心(不确定)+数学推导(确定)的能力,看来我的数学逻辑以及推理能力还需要加强啊。

题意不说,直接上思路:

由于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\n");    
    } else {
      printf("Yes\n"); 
      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("\n");
    }
  }
  return 0;
}

 

转载于:https://www.cnblogs.com/yifeiWa/p/11249887.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值