SPOJ_AMR10D_Soccer Teams(DP)

本文介绍了一种利用动态规划解决构造最短数使其能被11整除的问题的方法。通过定义状态dp[i][j][k]来表示用前i位数,有j个数放在奇数位上,模11等于k的情况,实现了高效求解。

题型:动态规划


题意:

      给出1~9这些数的个数,和任意多的0,要求把给出的1~9的数都用上,构成一个最短的数,使得这个数能够被11整除,求数的位数。


分析:

      若一个数能够被11整除,那么这个数的奇数位之和与偶数位之和的差能够被11整除。

      如果暴力的构造,二进制枚举,每个数放在奇位上或者不放,复杂度2^100,太大。

      那么其中会有很多无用的状态,这个时候,我们就可以想一下能不能用dp做。

      先不考虑数最短,先只考虑能否将所有的数全用上构造出能够被11整除的数。

      对于状态的定义,大概需要靠多学多做的经验。

      可以转换为类似于背包的问题。

      定义dp[i][j][k]表示用上前i位数,有j个数放在奇位上,模11等于k是否有解。

      因为ABS(奇位和-偶位和)%11==0

      有状态转移方程:

                         if(dp[i][j][k]有解)

                               r:0~a[i]

                                     dp[i][j+r][(k+i*r-i*(a[i]-r))%11]有解

       注意k+i*r-i*(a[i]-r)可能为负,需要+11直至非负。


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

#define mt(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;

int dp[12][121][12];
int a[12],s[12];

int ABS(int x) {
    if(x<0) return -x;
    return x;
}

int main() {
    int _;
    scanf("%d",&_);
    while(_--) {
        s[0] = 0;
        for(int i=1; i<=9; i++) {
            scanf("%d",&a[i]);
            s[i] = a[i] + s[i-1];
        }

        mt(dp,0);
        dp[0][0][0] = 1;
        for(int i=0; i<=8; i++) {
            for(int j=0; j<=s[i]; j++) {
                for(int k=0; k<=10; k++) {
                    if(dp[i][j][k]) {
                        for(int r=0; r<=a[i+1]; r++) {
                            int tmp = (i+1)*r-(a[i+1]-r)*(i+1);
                            while(tmp<0) tmp+=11;
                            dp[i+1][j+r][(k+tmp)%11] = 1;
                        }
                    }
                }
            }
        }


        int ans = inf;
        for(int i=0;i<=s[9];i++){
            if(dp[9][i][0]){
                ans = min(ans,max(2*i-1,2*(s[9]-i)));
            }
        }

        if(ans==inf) puts("-1");
        else printf("%d\n",ans);

    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值