bzoj1177&p3625 [APIO2009]采油区域p[大力讨论]
给定矩形,从中选出三个边长K的正方形互不重叠,使得覆盖到的数总和最大。
想的时候往dp上钻去了。。结果一开始想了一个错的dp,像这样
1 /************************************************************** 2 Problem: 1177 3 User: stealthassassin 4 Language: C++ 5 Result: Wrong_Answer 6 ****************************************************************/ 7 8 #include<iostream> 9 #include<cstdio> 10 #include<cstring> 11 #include<cmath> 12 #include<algorithm> 13 #define dbg(x) cerr<<#x<<" = "<<x<<endl 14 #define ddbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl 15 using namespace std; 16 typedef long long ll; 17 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;} 18 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;} 19 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 20 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 21 template<typename T>inline T _max(T A,T B,T C){return _max(A,_max(B,C));} 22 template<typename T>inline T read(T&x){ 23 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 24 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 25 } 26 const int N=1500+7; 27 ll sum[N][N]; 28 int f[N][N][3],n,m,A; 29 inline int get_sum(int xa,int ya,int xb,int yb){return sum[xb][yb]-sum[xa][yb]-sum[xb][ya]+sum[xa][ya];} 30 31 int main(){//freopen("test.in","r",stdin);freopen("test.out","w",stdout); 32 read(n),read(m),read(A); 33 for(register int i=1;i<=n;++i)for(register int j=1;j<=m;++j)sum[i][j]=read(sum[i][j])+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]; 34 for(register int k=1;k<=3;++k)for(register int i=A;i<=n;++i)for(register int j=A;j<=m;++j) 35 f[i][j][k]=_max(f[i-1][j][k],f[i][j-1][k],_max(f[i-A][j][k-1],f[i][j-A][k-1])+get_sum(i-A,j-A,i,j));//ddbg(i,j),ddbg(k,f[i][j][k]); 36 printf("%d\n",f[n][m][3]); 37 return 0; 38 }
结果WA。还发现竟然骗了40pts。很快就发现dp推得有问题。明显没有办法用简单的dp状态来表示,想了一会复杂的dp方法,想不出来,然后就觉得这题DP不可做。然后陷入了僵局,尝试各种乱七八糟的做法却无果。
之后翻题解才发现,你3个正方形,个数那么少,用不了dp,那明摆着就是让你研究他们相对位置关系嘛。。
所以下面大力讨论一下,最大答案就有了这几种可能分布情况(红线表示区块的分界):
所以只要对每种情况都找一下最大总和,具体就是枚举边界线,3456四种分别二重横竖枚举$O(nm)$,每个区块里找最大值矩形,这个可以用前缀和加递推提前预处理,看code。
12就枚举一条边界线,然后中间那块不妨是k宽度的也没问题,在k宽度中找一个最大的,再配上边上两块最大的。
所以这题代码真的是写的恶心。。。而且BZOJ数据有锅,用快读还超时。。做了近4小时,心态有点崩溃。。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #define dbg(x) cerr<<#x<<" = "<<x<<endl 7 #define ddbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl 8 #define rep(i,x,y) for(register int i=x;i<=y;++i) 9 #define per(i,x,y) for(register int i=x;i>=y;--i) 10 using namespace std; 11 typedef long long ll; 12 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;} 13 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;} 14 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 15 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 16 template<typename T>inline T _max(T A,T B,T C){return _max(A,_max(B,C));} 17 //namespace io 18 //{ 19 // const int SIZE = (1 << 21) + 1; 20 // char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr; 21 // #define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++) 22 // inline void flush (){fwrite (obuf, 1, oS - obuf, stdout);oS = obuf;} 23 // inline void putc (char x){*oS ++ = x;if (oS == oT) flush ();} 24 // template <class I> 25 // inline void read(I &x) {for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1; 26 // for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;} 27 // template <class I> 28 // inline void print (I x){ 29 // if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;while(x) qu[++ qr] = x % 10 + '0', x /= 10;while (qr) putc (qu[qr--]);} 30 // struct Flusher_ {~Flusher_(){flush();}}io_flusher_; 31 //} 32 //using io::read; 33 //using io::putc; 34 //using io::print; 35 const int N=1500+5; 36 int a[N][N],zs[N][N],ys[N][N],zx[N][N],yx[N][N],ans; 37 int n,m,k; 38 39 int main(){//freopen("tmp.in","r",stdin);freopen("tmp.out","w",stdout); 40 cin>>n>>m>>k; 41 rep(i,1,n)rep(j,1,m)scanf("%d",&a[i][j]),a[i][j]+=a[i][j-1]; 42 rep(i,1,n)rep(j,1,m)a[i][j]+=a[i-1][j]; 43 per(i,n,k)per(j,m,k)a[i][j]=a[i][j]-a[i-k][j]-a[i][j-k]+a[i-k][j-k]; 44 rep(i,k,n)rep(j,k,m)zs[i][j]=max(a[i][j],max(zs[i-1][j],zs[i][j-1])); 45 rep(i,k,n)per(j,m,k)ys[i][j]=max(a[i][j],max(ys[i-1][j],ys[i][j+1])); 46 per(i,n,k)rep(j,k,m)zx[i][j]=max(a[i][j],max(zx[i+1][j],zx[i][j-1])); 47 per(i,n,k)per(j,m,k)yx[i][j]=max(a[i][j],max(yx[i+1][j],yx[i][j+1])); 48 rep(i,k,n-2*k)rep(j,k,m)ans=max(ans,zs[i][m]+a[i+k][j]+yx[i+k*2][k]); 49 rep(i,k,m-2*k)rep(j,k,n)ans=max(ans,zs[n][i]+a[j][i+k]+yx[k][i+k*2]); 50 rep(i,k,n-k)rep(j,k,m-k)ans=max(ans,zs[i][j]+ys[i][j+k]+yx[i+k][k]); 51 rep(i,k,n-k)rep(j,k,m-k)ans=max(ans,zx[i+k][j]+yx[i+k][j+k]+zs[i][m]); 52 rep(i,k,m-k)rep(j,k,n-k)ans=max(ans,zs[j][i]+zx[j+k][i]+yx[k][i+k]); 53 rep(i,k,m-k)rep(j,k,n-k)ans=max(ans,ys[j][i+k]+yx[j+k][i+k]+zs[n][i]); 54 printf("%d\n",ans); 55 return 0; 56 }