【dp】Codeforces R167E.Dima and Figure
http://www.codeforces.com/contest/273/problem/D
给去一个 n*m的矩阵,让我们在其中去若干个块,使得其满足一下两个条件,A所有块连成一个整体,B.取出的块中任意两个(x1,y1),(x2,y2),可以通过其他块到达且最短路长度是abs(x1-x2)+abs(y1-y2),问一共有多少种取法。
稍微画一下可以发现对于某行,其被取的块必然是连在一起的,否则条件B显然不成立。所以用dp[i][j][k]表示状态,代表第i行取 第j~k块。
之后再考虑到要满足如果某行相对上一行j坐标出现向右偏移的情况,则以后j不能再向左偏移,否则B条件不成立。同理k坐标也有同样的性质。可以将状态扩充成dp[i][j][k][sta]。。其中sta是0~3的数字表示j向右偏移,和 k向左偏移是否已经出现过。。到此可以得到一个很暴力的O(n^5) 的做法,如下
View Code
1 for (int i = 1; i<=n; i++) 2 for (int j = 1; j<=m; j++){ 3 for (int k = j; k<=m; k++){ 4 dp1[i][j][k][0] += 1; 5 dp1[i][j][k][0] %= MOD; 6 Rep(c,4) { ans += dp1[i][j][k][c]; ans %= MOD; } 7 for (int g = 1; g<=m; g++) 8 for (int h = g; h<=m; h++){ 9 if ( max(j,g) > min(k,h) ) continue; 10 int sta = 0 , p = 0; 11 if ( g > j ) sta += 1; 12 if ( h < k ) sta += 2; 13 if ( g < j ) p += 1; 14 if ( h > k ) p += 2; 15 for (int c = 0; c<4; c++){ 16 if ( c&p ) continue; 17 dp1[i+1][g][h][c|sta] += dp1[i][j][k][c]; 18 dp1[i+1][g][h][c|sta] %= MOD; 19 } 20 } 21 } 22 }
验证一下小数据发现该dp是正确的,但是对于n<=150显然不足以AC,需要再优化,这里可以采用累加的方法将复杂度降到O(n^3), 比如dp[i][j][k][0] 对比其于dp[i][j][k-1][0],发现其差异 再我们按一定方法枚举j,k时,是可以利用上次运算的结果累加得到的。 同理分析,可以得到1,2,3的O(n^3)算法,详见程序。
View Code
1 //By Lin 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<set> 6 #include<map> 7 #include<queue> 8 9 #define sqr(x) ((x)*(x)) 10 #define MOD 1000000007 11 #define Rep(i,n) for(int i = 0; i<n; i++) 12 #define foreach(i,n) for( __typeof(n.begin()) i = n.begin(); i!=n.end(); i++) 13 #define X first 14 #define Y second 15 #define mp(x,y) make_pair(x,y) 16 17 using namespace std; 18 typedef long long LL; 19 typedef pair<int,int> pii; 20 21 #define N 155 22 int n,m,dp[N][N][N][4]; 23 int main(){ 24 scanf("%d%d", &n, &m ); 25 int ans = 0, ecnt = 0; 26 // for (int i = 1; i<=n; i++) 27 // for (int j = 1; j<=m; j++){ 28 // for (int k = j; k<=m; k++){ 29 // dp1[i][j][k][0] += 1; 30 // dp1[i][j][k][0] %= MOD; 31 // Rep(c,4) { ans += dp1[i][j][k][c]; ans %= MOD; } 32 // for (int g = 1; g<=m; g++) 33 // for (int h = g; h<=m; h++){ 34 // if ( max(j,g) > min(k,h) ) continue; 35 // int sta = 0 , p = 0; 36 // if ( g > j ) sta += 1; 37 // if ( h < k ) sta += 2; 38 // if ( g < j ) p += 1; 39 // if ( h > k ) p += 2; 40 // for (int c = 0; c<4; c++){ 41 // if ( c&p ) continue; 42 // dp1[i+1][g][h][c|sta] += dp1[i][j][k][c]; 43 // dp1[i+1][g][h][c|sta] %= MOD; 44 // } 45 // } 46 // } 47 // } 48 // 49 // printf("%d\n" , ans ); 50 ans = 0; 51 memset( dp , 0 , sizeof(dp) ); 52 int last[N]; 53 for (int i = 1; i<=n; i++) { 54 memset( last , 0 , sizeof(last) ); 55 for (int L = 1; L<=m; L++){ 56 for (int j = 1; j+L-1<=m; j++){ 57 int k = j+L-1; 58 dp[i][j][k][0] = dp[i][j][k-1][0]; 59 last[k] += dp[i-1][j][k][0]; 60 last[k] %= MOD; 61 dp[i][j][k][0] += last[k]; 62 dp[i][j][k][0] %= MOD; 63 if ( L == 1 ) dp[i][j][k][0] = i; 64 } 65 } 66 } 67 for (int i = 1; i<=n; i++) { 68 memset( last , 0 , sizeof(last) ); 69 for (int j = 1; j<=m; j++){ 70 for (int k = j; k<=m; k++ ) { 71 dp[i][j][k][1] = dp[i][j][k-1][1]; 72 last[k] += dp[i-1][j][k][1]; 73 last[k] %= MOD; 74 last[k] += dp[i-1][j-1][k][0]; 75 last[k] %= MOD; 76 dp[i][j][k][1] += last[k]; 77 dp[i][j][k][1] %= MOD; 78 } 79 } 80 } 81 for (int i = 1; i<=n; i++) { 82 for (int j = 1; j<=m; j++){ 83 for (int k = j; k<=m; k++ ) { 84 dp[i][j][k][2] = dp[i][m-k+1][m-j+1][1]; 85 } 86 } 87 } 88 for (int i = 1; i<=n; i++) { 89 memset( last , 0 , sizeof(last) ); 90 for (int L = m; L>=1; L--){ 91 for (int j = 1; j+L-1<=m; j++){ 92 int k = j+L-1; 93 if ( j == 1 || k == m ) continue; 94 dp[i][j][k][3] = dp[i][j][k+1][3]; 95 last[k] += dp[i-1][j-1][k+1][0]; 96 last[k] %= MOD; 97 last[k] += dp[i-1][j][k+1][1]; 98 last[k] %= MOD; 99 last[k] += dp[i-1][j-1][k][2]; 100 last[k] %= MOD; 101 last[k] += dp[i-1][j][k][3]; 102 last[k] %= MOD; 103 dp[i][j][k][3] += last[k]; 104 dp[i][j][k][3] %= MOD; 105 } 106 } 107 } 108 for (int i = 1; i<=n; i++) 109 for (int j = 1; j<=m; j++) 110 for (int k = j; k<=m; k++) 111 for (int c = 0; c<4; c++){ 112 ans += dp[i][j][k][c]; 113 ans %= MOD; 114 } 115 printf("%d\n" , ans ); 116 return 0; 117 }