【codechef】s=abs(这部分数-剩下的数),求所有选取方式的s之和

65 篇文章 0 订阅
47 篇文章 0 订阅

https://www.codechef.com/KOL15MOS/problems/KOL1502 印度的区域赛,很遗憾赛后不能提交了。。。

原本一直找不到正确的AC打开方式,实在也算道脑洞题吧。下午课上突然想到如何解题了,这种灵光一现的时刻多一点该有多好。。。

一组数据,从中选取一部分数,s=max(这部分数-剩下的数,剩下的数-这部分数),即abs(这部分数-剩下的数)。求所有选取方式的s之和。

一开始一直画画画,想找出什么规律,但是发现345和349这两种情况是的加减是不一样的,所以没有规律可循。然后就懵了两天。

今天课上突然想到,如果给一组数前面加上正负号,那么其中的正数之和如果小于s的一半,最后的结果就是负数。那么这个负数就要被提出来变成正数。如果不处理abs的话,那么所有选取方式的s之和就是0。原本是负的变成正的结果*2。

当然题目给的范围还是很人性化的(1 ≤ Taste scores of ingredients ≤ 10),这使我更坚定了用DP解题的信念。

#include <bits/stdc++.h>
#define ll long long
#define mod 10000000
using namespace std;
int x[1005];
ll dp[1005]; //dp[i]表示当前总和为i时的情况数
int main(){
    int t;
    cin>>t;
    while(t--){
    	memset(dp,0,sizeof(dp)); 
    	int n;
    	cin>>n;
    	ll s=0;
    	for(int i=1;i<=n;++i){
    		cin>>x[i];
    		s+=x[i];
    	}
    	int g=s/2+s%2;
    	dp[0]=1;
    	for(int i=1;i<=n;++i){
    		for(int j=g;j>=0;--j){
    			if(j+x[i]>g)
    				continue;
    			dp[j+x[i]]+=dp[j];
    		}
    	}
    	ll ss=0; 
    	for(int i=0;i<g;++i){
    		ss+=2*dp[i]*(s-i-i);
    	}
    	cout<<ss<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
我们可以先考虑如何计算一个正整的约之和。设 $n$ 是一个正整,$d$ 是 $n$ 的一个约,则 $n/d$ 也是 $n$ 的一个约。因此,我们可以将 $n$ 的所有约分成两组:小于 $\sqrt{n}$ 的约和大于 $\sqrt{n}$ 的约。对于小于 $\sqrt{n}$ 的约 $d$,可以出 $n/d$,然后将它们的和累加起来;对于大于 $\sqrt{n}$ 的约 $d$,可以出 $d$,然后将它们的和累加起来。这样就可以计算出 $n$ 的约之和。 接下来,我们考虑如何选取若干个不同的正整,使得它们的和不超过 $S$,并且它们的约之和最大。我们可以使用动态规划来解决这个问题。 设 $f(i, j)$ 表示选取前 $i$ 个不同的正整,它们的和不超过 $j$,并且它们的约之和最大的值。则对于第 $i$ 个正整,它可以选也可以不选。如果它不选,那么 $f(i,j) = f(i-1,j)$;如果它选,则 $f(i,j)$ 的值为前 $i-1$ 个正整中,和不超过 $j-i$ 的所有方案中,约之和最大的方案再加上第 $i$ 个正整的约之和。因此,可以得到状态转移方程: $$f(i,j) = \max(f(i-1,j), f(i-1,j-i) + \sigma(i))$$ 其中,$\sigma(i)$ 表示第 $i$ 个正整的约之和。初始状态为 $f(0,j) = 0$(选取 0 个正整),$f(i,0) = 0$(和为 0)。 最终的答案为 $f(n,S)$。时间复杂度为 $O(nS\sqrt{S})$。 代码实现如下: ```python def divisor_sum(n): """ 计算 n 的约之和 """ s = 0 for i in range(1, int(n**0.5)+1): if n % i == 0: s += i if i != 1 and i**2 != n: s += n // i return s def max_divisor_sum(n, S): """ 选取和不超过 S 的若干个不同的正整,使得所有的约(不含它本身)之和最大 """ f = [[0] * (S+1) for _ in range(n+1)] for i in range(1, n+1): for j in range(1, S+1): f[i][j] = f[i-1][j] if j >= i: f[i][j] = max(f[i][j], f[i-1][j-i] + divisor_sum(i)) return f[n][S] ``` 其中,n 和 S 分别为题目中的两个参,返回值为所有的约之和的最大值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值