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

 

转载于:https://www.cnblogs.com/lzqxh/archive/2013/02/21/2921043.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值