hdu5389 Zero Escape

题意:首先告诉你一个东西叫数位根,就是把这个数每一位加起来得到一个数,如果不是1到9就再把得到的数每一位加起来,直到变成1到9。然后现在有N个1到9的数,另外给两个1到9的数A和B,让你把这N个数分成两组,问有多少方案使其中一组的和的数位根为A,另一组为B,所有数的数位根为A或B也算到方案总数里面。

我是用DP做的,一开始定义dp[i][a][b]为把前i个数分两组,第一个数所在那组得到的数位根是A,另一组得到的数位根是B,不过这样是会超时的。

后来队友发现了个性质就是前i个数的和是已经确定的,假如分成两组,一组的数位根确定下来之后另一组的数位根是唯一对应的,那么就可以把后面一维给省掉了。

定义get函数为获取数位根,那么转移就是:

dp[i][get(j+num[i])]+=dp[i-1][j];

dp[i][j]+=dp[i-1][j];

dp[i][get(sum[i-1])]+=1;(前i-1个数一组,第i个数单独一组)

我还用了滚动数组实现,不过好像没什么必要。

至于题解说的直接模9完全没有发现,而且当时手残get函数还出了点小bug浪费了很多时间,不过一发就过了,过了就好吧。


代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<iomanip>
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<list>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

#define rep(i,k,n) for(int i=(k);i<=(n);i++)
#define red(i,k,n) for(int i=(k);i>=(n);i--)
#define sqr(x) ((x)*(x))
#define clr(x,y) memset((x),(y),sizeof(x))
#define mod 258280327
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)>(b)?(b):(a))
//const int MAXN = ;

LL dp[2][12];
int now,last;
int n,A,B;
int num[100010];
int sum[100010];
int pre[100010];

int get(int x)
{
    if(x<=0)return 0;
    int ret=x;
    while(ret==0||ret>9)
    {
    x=ret;
    ret=0;
    while(x)
    {
        ret+=x%10;
        x/=10;
    }

    }
    return ret;
}

int main()
{
//#define LOCAL
#ifdef LOCAL
    freopen("e:\\read.txt","r",stdin);
    //freopen("e:\\write.txt","w",stdout);
#endif
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d%d%d",&n,&A,&B);
        rep(i,1,n)
        {
            scanf("%d",&num[i]);
            sum[i]=sum[i-1]+num[i];
            pre[i]=get(sum[i]);
        }
        LL ans=0;
        if(get(sum[n])==A)ans++;
        if(get(sum[n])==B)ans++;
        clr(dp,0);
        dp[0][num[1]]=1;
        last=1;
        now=0;
        int t1,t2;
        rep(i,3,n)
        {
            last=now;
            now^=1;
            clr(dp[now],0);
            rep(j,1,9)if(dp[last][j])
            {
                t1=get(j+num[i]);
                dp[now][t1]=(dp[now][t1]+dp[last][j])%mod;
                dp[now][j]=(dp[now][j]+dp[last][j])%mod;
            }
            dp[now][pre[i-1]]=(dp[now][pre[i-1]]+1)%mod;
        }
        if(get(sum[n]-A)==B)ans=(ans+dp[now][A])%mod;
        if(get(sum[n]-B)==A)ans=(ans+dp[now][B])%mod;
        cout<<ans<<endl;
    }


    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值