题意:某工厂生产一种锁,锁上面有n条槽,现要求生产出来的锁,满足下述条件:
1. 槽深 一定是{1,2,3,4}中的某个值
2. 至少存在 一对相邻的槽,两槽的深度差等于3
3. 至少存在 3种不同深度的槽。
问:已知n,求有多少中排列组合方式满足上述条件
样例:
2:0 因为只有2个槽,不满足条件3;
3:8 3个槽满足的方案有 : 142 、143、 241 、214 、341 、314 、413 、412
解题:数位dp的思想,我们把每个方案 看成一个数字 比如142 就说明 第一个槽深是1,第二个槽深是4......
如果有3个槽,那么满足约束条件的方案 一定落在 111 ~ 444 之间,这样数位dp的范围就有了,
满足条件1,我们只需要在枚举的时候只1~4的范围即可,要满足条件3,在枚举过程中计算种类即可
要满足条件2,只要数字中出现连续的 14 或者 41的情况即可。
接下来就是找状态
状态:ll dp[i][j][k][l]; 表示 枚举到第i位,j表示前面有无出现14/41,k表示前一位的数字是k,l表示前面出现了l种数字 的情况数!
找到状态,接下来就容易做了...
#include<stdio.h>
#include<memory.h>
typedef long long ll;
const int maxn = 17;
ll dp[maxn+1][2][5][5];
ll x[maxn+10];//枚举到pos位,前一位的数字是pre,find表示有无找到14或者41的情况,ff[]标志该数字有无出现过,kind出现了几种数字
ll dfs(int pos,ll pre,bool find,bool ff[5],int kind,bool limit)
{
if(pos==-1) return (find && kind >=3);
if(!limit && dp[pos][find][pre][kind]!=-1) return dp[pos][find][pre][kind];
ll up = limit?x[pos]:4;
ll rs = 0;
for(ll i=1;i<=up;i++)
{
if(ff[i]==false)
{
ff[i] = true;
if((pre==1 && i==4) || (pre==4 && i==1))
{
rs = rs + dfs(pos-1,i,true,ff,kind+1,limit&&i==up);
}else{
rs = rs + dfs(pos-1,i,find,ff,kind+1,limit&&i==up);
}
ff[i] = false;
}else if(ff[i]==true){
if((pre==1 && i==4) || (pre==4 && i==1))
{
rs = rs + dfs(pos-1,i,true,ff,kind,limit&&i==up);
}else{
rs = rs + dfs(pos-1,i,find,ff,kind,limit&&i==up);
}
}
}
if(!limit) dp[pos][find][pre][kind] = rs;
return rs;
}
ll solve(ll xx)
{
int pos = 0;
while(xx)
{
x[pos++] = xx%10;
xx = xx/10;
}
bool ff[5]={false};
return dfs(pos-1,0,false,ff,0,true);
}
int main()
{
int n;
memset(dp,-1,sizeof(dp));
while(true)
{
scanf("%d",&n);
if(n==-1) break;
ll a=0,b=0;
for(int i=1;i<=n;i++)
{
a = a*10ll + 1ll;
b = b*10ll + 4ll;
}
// printf("a == %lld,b==%lld\n",a,b);
printf("%d: %lld\n",n,solve(b)-solve(a));
}
return 0;
}//0ms
努力吧,渣科