清橙A1235(BZOJ2147) Digit(蒋中天)

来源:互联网 发布:判断上三角矩阵 编辑:程序博客网 时间:2024/06/04 08:19
问题描述
  在数学课上,小T又被老师发现上课睡觉了。为了向全班同学证明小T刚才没有好好听课,数学老师决定出一道题目刁难一下小T,如果小T答不出,那么……
  情节就按照俗套的路线发展下去了,小T显然无法解决这么复杂的问题,可怜的小T只能向你求助:
  题目是这样的:
  求一个满足条件的n位数A(不能有前导0),满足它的数字和为s1,并且,A*d的数字和为s2.
输入格式
  一行四个整数:n, s1, s2, d
输出格式

  若存在最小的满足条件的数,则输出这个数,否则输出-1。

初见的时候想了一会儿数位DP,但是想了半天想不出来,复杂度大概为O(n^3*8100).

后来发现了一个性质,就是乘d后的值与乘d前相差一定是⑨的倍数,所以第二维可以缩小10倍,然而这并没有什么用.

考虑降维,因为前面那个方法的dp值是0或1,看起来很浪费.发现如果可以有前导0,所用的位数越少越好.

直接转移运算量大概是1e8级别的,考虑用BFS方式转移,结合前面的性质,时间复杂度就是1e7级别的.

最后从高位到低位一位位确定这个数.注意第一位不能是0,这一步的复杂度是O(n*100)

总复杂度是O(n^2*8100/10),瓶颈是DP

那么,具体细节看代码.

UPD:我居然选错语言了

#include<cstdio>#include<iostream>#include<ctime>#include<queue>using namespace std;struct node{int x,y,z;};queue<node> q;int d;int dis[1000][1000][10];//第一维是乘d前的数位和,第二维是乘d后的数位和,第三维是进位.bool vis[1000][1000][10];bool ok(int x,int y,int z,int n){if(x<0||y<0) return 0;if(!vis[x][y][z]) return 0;return dis[x][y][z]<=n;}void solve(int ls,int s1,int s2,int s3,int lef) //ls是上一位乘d后最后一位的值,s1是乘d前位数和,s2是乘d后数位和,s3是乘d后再多进一位的数位和{int sw,gw;bool bo;int ss,sss;int nw=0;while(lef){bo=0;for(int i=0;i<10;i++){for(int j=0;j<10;j++){ss=d*i+j;sw=ss/10,gw=ss%10;if(sw+ls<10){if(ok(s1-i,s2-sw-gw,j,lef-1)){bo=1;break;}}else{if(ok(s1-i,s3-sw-gw,j,lef-1)){bo=1;break;}}}if(bo){nw=i;break;}}s1-=nw;sw=(nw*d)/10,gw=(nw*d)%10;if(sw+ls<10)ss=s2-sw-gw;elsess=s3-sw-gw;if(sw+ls+1<10)sss=s2-sw-gw+9;elsesss=s3-sw-gw+9;//printf("%d %d %d %d %d %d\n",s1,s2,s3,sw,gw,sw+ls<10);s2=ss,s3=sss;printf("%d",nw);ls=gw;lef--;}printf("\n");}int main(){//freopen("A1235.in","r",stdin);//freopen("A1235.out","w",stdout);int n,s1,s2;scanf("%d%d%d%d",&n,&s1,&s2,&d);dis[0][0][0]=0;vis[0][0][0]=1;q.push((node){0,0,0});node st;int xx,yy,z,id;while(!q.empty()){st=q.front();q.pop();for(int i=0;i<10;i++){xx=st.x+i;z=i*d+st.z;yy=st.y+(z%10);id=z/10;if(xx>=1000||yy>=1000) continue;if(!vis[xx][yy][id]){vis[xx][yy][id]=1;dis[xx][yy][id]=dis[st.x][st.y][st.z]+1;if(dis[xx][yy][id]<n)q.push((node){xx,yy,id});}}}z=-1;for(int i=1;i<=9;i++){for(int j=0;j<10;j++){xx=(i*d+j)%10+(i*d+j)/10;//if(i==1) printf("%d %d %d\n",j,s1-i,s2-xx);if(ok(s1-i,s2-xx,j,n-1)){z=i;break;}}if(z!=-1) break;}if(z==-1){printf("-1\n");return 0;}printf("%d",z);int ss=(z*d)%10+(z*d)/10;solve((z*d)%10,s1-z,s2-ss,s2-ss+9,n-1);//cerr<<clock()*1.0/CLOCKS_PER_SEC<<endl;return 0;}