Atcoder Beginner Contest 044

C.C - Tak and Cards

我一开始想的是先从小到大排个序,然后分情况,先从左往右一个数一个数枚举,如果等于ave*1,就res++,如果大于ave*1,就说明1个数的没有了,然后从左到右两个数两个数枚举,如果等于ave*2,就res++,如果大于ave*2,就说明两个数的没有了,以此类推

但是忽略了不一定非得相邻的两个数相加,这个思路漏洞很大,不能采用

实际上,像这种平均数的问题,可以所有数都减去一个平均数,然后在这些数里选择,加起来等于0的,那么它们的原始数的平均数就是该平均数,这里暂且先不这么做 

有限制的选择,背包问题

f[i][j][k]表示从前i张卡片中选,选择j张,总和是k的方案数

状态转移:在前i张卡片中选,选择j张,总和是k的方案数可以是选i与不选i的方案数总和

不选i,在前i-1张中选,选择j张,总和为k;

选i,在前i-1张中选,选择j-1张,总和为k-a[i] 

AC代码: 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 110;
LL a[N];
LL f[N][N][3000];
LL res;
int main()
{
    int n,ave;
    cin >> n >> ave;
    for (int i = 1; i <= n; i++) cin >> a[i];
    f[0][0][0] = 1;//f[0][0][0]=1是因为这一状态用于转移到后面的状态,如果由这一状态转移,肯定要加1的,不可能加0
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= i;j++ ) {
            for (int k = 0; k <=2500; k++) {
                f[i][j][k] += f[i - 1][j][k];
                if (k - a[i] >= 0 && j >= 1) f[i][j][k] += f[i - 1][j - 1][k - a[i]];
            }
        }
    }
    for (int j = 1; j <= n; j++) {
        res += f[n][j][j*ave];
    }
    cout << res << endl;
    return 0;
}

D.D - Digit Sum 

题意:将n转化成b进制,然后对每一位求和,看是否等于s 

分类:

1.s等于n,b=n+1

2.s大于n,无解

3.s小于n

a0*b^0+a1*b^1+...+ak*b^k=n

a0+a1+...+ak=s

当k>=2时,b<=sqrt(n),暴力枚举即可

当k=1时,b>sqrt(n),a0*b^0+a1*b^1=n==>a0+a1*b=n

a0+a1=s

所以b=(n-s)/a1+1,因为a1<b,所以a1<=sqrt(n-s)

接着只需要枚举 n - s 的合法性判断是否存在即可

注意合法性判断既要满足 x1,x2 大于等于 0,也要小于 b(x1,x2即n在b进制下第一位和第二位)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define int long long
int n, s;
bool check(int x) {
    int res = 0;
    int m = n;
    while (m) {
        res += m % x;
        m /= x;
    }
    return res == s;
}
signed main()
{
    cin >> n >> s;
    int sum;
    if (s == n) {
        cout << n + 1 << endl;
        return 0;
    }
    if (s > n) {
        cout << -1 << endl;
        return 0;
    }
    for (int i = 2; i <= n/i; i++) {
        if (check(i)){
            cout << i << endl;
            return 0;
        }
    }
    int ans = 1e18;
    int k = n - s;
    for (int i = 1; i<=k/i; i++) {
        if(k%i==0){
            int b = k / i + 1, x0 = s - i;
            if (b >= n / b && x0 >= 0 && x0 < b &&i<b&& b >= 2) {
                ans = min(ans, b);
            }
        }
    }
    if (ans != 1e18) cout << ans << endl;
    else cout << -1 << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值