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; }