TC SRM 671 Div2 1000 BearDestroysDiv2

来源:互联网 发布:pp软件源地址 编辑:程序博客网 时间:2024/06/09 20:08

Problem Statement

Limak is a big grizzly bear. He is now going to destroy an entire forest.

Limak’s forest is a rectangular grid that consists of W columns by H rows of cells. At the beginning a single tree grows in each cell. The forest is aligned with the major compass directions: row numbers increase towards the South and column numbers increase towards the East.

Limak will destroy the forest by pushing some of the trees. Whenever Limak pushes a tree, the tree will fall down and remain lying both in the current cell and in the next cell in the direction in which it was pushed. For example, if Limak pushes the tree that grows in the cell (r,c) southwards, he will obtain a toppled tree that lies in the cells (r,c) and (r+1,c).

When pushing the trees, Limak always follows a few rules:

He only pushes trees in two directions: either southwards or eastwards.
He will never push a tree in a way that would cause it to fall out of the forest. For example, he will never push a tree in the last column eastwards.
He will never push a tree in a way that would produce two fallen trees lying on the same cell.

There is a single letter written on each of the trees. Each of these letters is either S or E (representing South and East, respectively). When pushing a tree, Limak will prefer the direction that is written on the tree. For example, if a tree has the letter S, Limak will push it southwards if possible.

Limak is going to visit each cell in the forest exactly once, in row major order. I.e., first he will visit all the cells in the first row from left to right, then the cells in the second row from left to right, and so on. In each cell he will act according to the following algorithm:

Is there a fallen tree in the current cell? If yes, there is no room here to do anything, so I’ll just move to the next cell.
Can I push the tree in the direction that is given by the letter written on the tree? If yes, I’ll do so and move to the next cell.
Can I push the tree in the other direction? If yes, I’ll do so and move to the next cell.
I’ll move to the next cell without pushing the tree.

See Example 0 for a sample execution of Limak’s algorithm.

You are given the ints W and H. There are 2^(W*H) different forests with these dimensions. (Different forests have different assignments of letters S and E to the trees.) For each of these forests, compute the number of trees Limak would topple. Return the sum of those 2^(W*H) numbers, modulo MOD.

Definition

Class BearDestroysDiv2
Method sumUp
Parameters int , int , int
Returns int
Method signature int sumUp(int W, int H, int MOD)
(be sure your method is public)
Limits

Time limit (s) 2.000
Memory limit (MB) 256
Constraints

W will be between 1 and 7, inclusive.
H will be between 1 and 40, inclusive.
MOD will be between 3 and 10^9, inclusive.
MOD will be prime.
Test cases

W 4
H 3
MOD 999999937
Returns 24064
There are 2^(4*3) = 2^12 = 4096 different forests with W=4 columns and H=3 rows. One of those forests looks as follows:
SEEE
ESSS
EESS
When destroying this forest, Limak will push five trees. In the scheme below, the final locations of the toppled trees are shown using the numbers 1 through 5. The trees are numbered in the order in which Limak pushed them. The two cells that do not contain a fallen tree at the end are denoted by underscores.
1223
1453
_45_
It can be shown that for these dimensions there are exactly 512 forests in which Limak would topple exactly 5 trees. In each of the remaining (4096-512) forests Limak would topple 6 trees. Thus, the return value is 512 * 5 + (4096-512) * 6.

W 3
H 4
MOD 999999937
Returns 24576
For these dimensions of the forest Limak will always topple exactly 6 trees. The return value is 6 * 2^12.

W 2
H 20
MOD 584794877
Returns 190795689
For these dimensions of the forest Limak will always topple exactly 20 trees. The return value is (20 * 2^40) modulo MOD.

W 5
H 10
MOD 3
Returns 2

W 1
H 1
MOD 19

Returns 0
W 7
H 40
MOD 312880987
Returns 256117818

解析
状压DP

=======我是废话分割线========
感觉应该很简单的,但是我居然调了6个小时TAT
主要的转移方程和动规框架都没有问题,就是推算下个状态的模拟过程错了!!!!这个代码能力也是醉了!
后来还发现数组开小了,害得我查了好久!!!


p.s.如果还不会轮廓线动态规划请戳这里刘汝佳的《算法竞赛入门经典——训练指南》6.1节有专题讲解。
以下题解默认你已经掌握轮廓线动态规划
dp[i][j] 表示第i行,轮廓线为j的方案数,就是wswss地图的方案数。
cut[i][j] 表示第i行,轮廓线为j的状态下已经推倒的树的个数,就是wswss地图的方案数*该方案已经推倒的树的个数,即题目里面求的答案。p.s.如果一棵倒下的树横跨两行,我把它算在下面的一行里,上面的一行没有算这个树。

#include <cstdio>#include <cmath>#include <cstring>#include <ctime>#include <iostream>#include <algorithm>#include <set>#include <vector>#include <sstream>#include <typeinfo>#include <fstream>using namespace std;#define INF 0x3f3f3f3f3f3f3f3ftypedef unsigned long long LL;LL dp[50][1<<7],cut[50][1<<7];//i row, j is the statu of ith row. dp is num of methods. cut is the number of tree have been cut downclass BearDestroysDiv2 {    public:        void add(LL &a,LL b,int MOD)        //{a=(a+b>=MOD)?a+b-MOD:a+b;}        {a=(a%MOD+b%MOD)%MOD;}    int sumUp(int W, int H, int MOD) {        memset(dp,0x3f,sizeof(dp));//INF is illegal statu        memset(cut,0,sizeof(cut));        dp[0][(1<<W)-1]=1;        for(int i=1;i<=H;i++)//i row        {            for(int map=0;map<(1<<W);map++)//the map WSWWSS 101100                for(int last=0;last<(1<<W);last++)//the outline of last row                {                    if(dp[i-1][last]==INF)continue;                    int now=(1<<W)-1;LL num=0;                    for(int k=1;k<(1<<W);k=k<<1)                        if(last&k)//no influence                        {                            if(!(map&k))//to south                            {                                if(i<H)goto south;                                else if(k<1<<(W-1) && last&(k<<1)) goto west;                            }                            else                            {                                if(k<1<<(W-1) && last&(k<<1)) goto west;                                else if(i<H) goto south;                            }                            continue;west:                            num++;k=k<<1; continue;south:                            now-=k; continue;                        }                        else num++;                    if(dp[i][now]==INF) dp[i][now]=0;                    add(dp[i][now],dp[i-1][last],MOD);                    add(cut[i][now],cut[i-1][last]+((num%MOD)*(dp[i-1][last]%MOD))%MOD,MOD);                }        }        return (int)cut[H][(1<<W)-1];    }};
0 0
原创粉丝点击