BZOJ3324 : [Scoi2013]火柴棍数字

为了使数字最大,首先要最大化其位数。

设$f[i][j][k]$表示从低到高考虑了$i$位,手头火柴棍个数为$j$,第$i$位是不是$0$时,最少移动多少根火柴。

若$f[i][0][非0]\leq k$,则存在一个长度为$i$的数,由此可以求出最大长度。

确定长度之后,再从高到低逐位贪心确定每一位即可。

时间复杂度$O(nk)$。

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3510;
int n,m,all,use,len,i,j,k,x;char a[N];
short f[N/2][N*2][2];
int S[10]={126,48,107,121,53,93,95,112,127,125},dx[11][10],dy[11][10];
inline void up(short&x,short y){if(x>y)x=y;}
int main(){
  for(i=0;i<10;i++)for(j=0;j<10;j++)for(k=0;k<7;k++){
    if(!(S[i]>>k&1)&&(S[j]>>k&1))dx[i][j]++,dy[i][j]++;
    if((S[i]>>k&1)&&!(S[j]>>k&1))dx[i][j]--;
  }
  for(i=0;i<10;i++)for(k=0;k<7;k++)if(S[i]>>k&1)dx[10][i]++,dy[10][i]++;
  scanf("%s%d",a+1,&m);
  n=strlen(a+1);
  reverse(a+1,a+n+1);
  for(i=1;i<=n;i++)a[i]-='0';
  for(i=1;i<=n;i++)all+=dy[10][a[i]];
  len=all/2;
  for(i=n+1;i<=len;i++)a[i]=10;
  for(i=0;i<=len;i++)for(j=-m;j<=m;j++)for(k=0;k<2;k++)f[i][j+N][k]=m+1;
  f[0][N][0]=0;
  for(i=0;i<len;i++)for(j=-m;j<=m;j++)for(k=0;k<2;k++)if(f[i][j+N][k]<=m)
    for(x=0;x<10;x++){
      up(f[i+1][j+N+dx[a[i+1]][x]][x==0],f[i][j+N][k]+dy[a[i+1]][x]);
    }
  while(f[len][N][0]>m)len--;
  for(i=0;i<=len;i++)for(j=-m;j<=m;j++)up(f[i][j+N][0],f[i][j+N][1]);
  for(all=0,i=len;i;i--)for(j=9;~j;j--){
    all+=dx[a[i]][j],use+=dy[a[i]][j];
    if(f[i-1][N-all][0]<=m-use){printf("%d",j);break;}
    all-=dx[a[i]][j],use-=dy[a[i]][j];
  }
  return 0;
}

  

posted @ 2017-07-04 01:41  Claris  阅读(672)  评论(1编辑  收藏  举报