题型:动态规划
题意:
给出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;
}