Codeforces 677E Vanya and Balloons(DP + 一些技巧)
题目大概说给一张地图,地图每个格子都有0到9中的某一个数字。现在要在一个格子放炸弹,炸弹爆炸后水柱有两种扩展方式,一种是上、下、左、右,另一种是左上、右下、右上、左下,且四个方向的长度都一样。问放哪个格子怎么爆炸使得水柱覆盖的格子上的数字乘积最大,结果模1e9+7。
这题不会做。。
- 首先,各个格子的值取对数,这个为了比大小,因为需要模数,通过取对数缩小值。另外也把乘法转化成加法。这是个挺经典的技巧。
- 接下来,水柱的话肯定不能延长到0,不然功亏一篑,那么利用DP求出各个格子向8各个方向能延长多长,即dp[dir][x][y]。转移就是格子(x,y)不为0,从dp[dir][x+d[dir]][y+d[dir]]+1这儿转移,否则值为0。具体我是用记忆话搜索实现的。
- 然后知道各个格子8个方向能延长最长长度,上、下、左、右四个方向能够延长的最小值就是第一种水柱的拓展最优的长度,而左上、右下、右上、左下四个方向的最小值就是第二种了。
- 如何快速得出各个方向水柱覆盖的数字和——利用前缀和!这也是个挺经典的技巧。而取对数把乘法转化成加法,这就使得能利用前缀和的差求区间和。那么预处理出各个方向的前缀和就能在O(1)得出区间和了,即水柱覆盖到的格子的和。
- 最后就是通过枚举每个格子,得到各个格子放炸弹最多能得到的数字和,更新答案。
注意最后的答案不要直接求次幂还原。。会有精度问题。。应该要记录下来,在原地图中把各个数字累乘出答案。。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 using namespace std; 6 7 int n; 8 double a[1111][1111]; 9 int b[1111][1111]; 10 //上、下、右、左、左上、右下、右上、左下 11 int dx[]={-1,1,0,0,-1,1,-1,1}; 12 int dy[]={0,0,1,-1,-1,1,1,-1}; 13 14 int d[8][1111][1111]; 15 int dp(int dir,int x,int y){ 16 if(a[x][y]<0) return d[dir][x][y]=0; 17 if(d[dir][x][y]!=-1) return d[dir][x][y]; 18 int nx=x+dx[dir],ny=y+dy[dir],res=1; 19 if(nx>=1 && nx<=n && ny>=1 && ny<=n) res+=dp(dir,nx,ny); 20 return d[dir][x][y]=res; 21 } 22 23 double sum[4][2222][1111]; 24 int rec1[1111][1111],rec2[1111][1111]; 25 26 int main(){ 27 scanf("%d",&n); 28 for(int i=1; i<=n; ++i){ 29 for(int j=1; j<=n; ++j){ 30 scanf("%1d",&b[i][j]); 31 if(b[i][j]==0) a[i][j]=-1; 32 else a[i][j]=log2(b[i][j]); 33 } 34 } 35 for(int i=1; i<=n; ++i){ 36 for(int j=1; j<=n; ++j){ 37 sum[0][i][j]=sum[0][i][j-1]+a[j][i]; 38 } 39 } 40 for(int i=1; i<=n; ++i){ 41 for(int j=1; j<=n; ++j){ 42 sum[1][i][j]=sum[1][i][j-1]+a[i][j]; 43 } 44 } 45 for(int i=n; i>=1; --i){ 46 int x=i,y=1; 47 for(int j=1; j<=n-i+1; ++j){ 48 sum[2][n-i+1][j]=sum[2][n-i+1][j-1]+a[x][y]; 49 rec1[x][y]=j; 50 ++x; ++y; 51 } 52 } 53 for(int i=2; i<=n; ++i){ 54 int x=1,y=i; 55 for(int j=1; j<=n-i+1; ++j){ 56 sum[2][n+i-1][j]=sum[2][n+i-1][j-1]+a[x][y]; 57 rec1[x][y]=j; 58 ++x; ++y; 59 } 60 } 61 for(int i=1; i<=n; ++i){ 62 int x=1,y=i; 63 for(int j=1; j<=i; ++j){ 64 sum[3][i][j]=sum[3][i][j-1]+a[x][y]; 65 rec2[x][y]=j; 66 ++x; --y; 67 } 68 } 69 for(int i=2; i<=n; ++i){ 70 int x=i,y=n; 71 for(int j=1; j<=n-i+1; ++j){ 72 sum[3][n+i-1][j]=sum[3][n+i-1][j-1]+a[x][y]; 73 rec2[x][y]=j; 74 ++x; --y; 75 } 76 } 77 memset(d,-1,sizeof(d)); 78 for(int k=0; k<8; ++k){ 79 for(int i=1; i<=n; ++i){ 80 for(int j=1; j<=n; ++j){ 81 dp(k,i,j); 82 } 83 } 84 } 85 double ans=-1; 86 int ansx,ansy,ansdir=-1,anslen; 87 for(int i=1; i<=n; ++i){ 88 for(int j=1; j<=n; ++j){ 89 if(a[i][j]<0) continue; 90 91 int len=min(min(d[0][i][j],d[1][i][j]),min(d[2][i][j],d[3][i][j])); 92 double res=0; 93 res+=sum[0][j][i]-sum[0][j][i-len]; 94 res+=sum[0][j][i+len-1]-sum[0][j][i]; 95 res+=sum[1][i][j-1]-sum[1][i][j-len]; 96 res+=sum[1][i][j+len-1]-sum[1][i][j]; 97 if(ans<res){ 98 ans=res; 99 ansx=i; ansy=j; ansdir=0; anslen=len; 100 } 101 102 len=min(min(d[4][i][j],d[5][i][j]),min(d[6][i][j],d[7][i][j])); 103 res=0; 104 res+=sum[2][n-i+j][rec1[i][j]]-sum[2][n-i+j][rec1[i][j]-len]; 105 res+=sum[2][n-i+j][rec1[i][j]+len-1]-sum[2][n-i+j][rec1[i][j]]; 106 res+=sum[3][i+j-1][rec2[i][j]-1]-sum[3][i+j-1][rec2[i][j]-len]; 107 res+=sum[3][i+j-1][rec2[i][j]+len-1]-sum[3][i+j-1][rec2[i][j]]; 108 if(ans<res){ 109 ans=res; 110 ansx=i; ansy=j; ansdir=1; anslen=len; 111 } 112 } 113 } 114 if(ansdir==-1){ 115 printf("0"); 116 return 0; 117 } 118 long long res=1; 119 if(ansdir==0){ 120 for(int i=ansx-anslen+1; i<=ansx+anslen-1; ++i){ 121 res*=b[i][ansy]; 122 res%=1000000007; 123 } 124 for(int i=ansy-anslen+1; i<=ansy+anslen-1; ++i){ 125 if(i==ansy) continue; 126 res*=b[ansx][i]; 127 res%=1000000007; 128 } 129 }else{ 130 int x=ansx-anslen+1,y=ansy-anslen+1; 131 for(int i=1; i<anslen*2; ++i){ 132 res*=b[x][y]; 133 res%=1000000007; 134 ++x; ++y; 135 } 136 x=ansx-anslen+1; y=ansy+anslen-1; 137 for(int i=1; i<anslen*2; ++i){ 138 if(x==ansx && y==ansy){ 139 ++x; --y; 140 continue; 141 } 142 res*=b[x][y]; 143 res%=1000000007; 144 ++x; --y; 145 } 146 } 147 printf("%lld",res); 148 return 0; 149 }