zoukankan      html  css  js  c++  java
  • 洛谷 模拟城市2.0

    一次洛谷月赛的T1,当时因为是信心赛,认为第一题应该不会太难,结果想了很久,直接额放弃正解选择暴力。。。简直就是巨坑的五维DP。。。mmd

    题目背景

    博弈正在机房颓一个叫做《模拟城市2.0》的游戏。

    2048年,经过不懈努力,博弈终于被组织委以重任,成为D市市委书记!

    他勤学好问,励精图治,很快把D市建设成富强民主文明和谐的美好城市。为了进一步深化发展,他决定在海边建立一个经济开发区。

    题目描述

    已知开发区的建筑地块是一个n×n的矩形,而开发区可以建造三种建筑: 商业楼,住宅楼,教学楼。这任何两座建筑可以堆叠,可以紧密相邻。他需要建造正好a座商业楼,b座住宅楼,c座教学楼。但是,城市建成后要应付检查,如果安排的太混乱会被批评。不过幸运的是,只有一条公路经过了该开发区的一侧,就是说,检察人员全程只能看到开发区的一面。

    因此,他需要使得开发区建成后,从正面看去,只有一种类型的建筑。

    一共有多少种满足条件的方案呢? 请输出方案数,并对10^9+7取模。

    注意,对于同一个n,会有多组数据。

    输入输出格式

    输入格式:

    第一行两个整数n,T

    接下来T行,每行三个整数,表示该组数据的a,b,c

    输出格式:

    输出共T行,每行一个整数:表示各数据答案取模10^9+7的结果。

    输入输出样例

    输入样例#1: 
    2 1
    1 1 0
    输出样例#1: 
    4
    输入样例#2: 
    2 1
    2 1 0
    输出样例#2: 
    8

    说明

    对于20%的数据,n2  a,b,c3  T5

    对于另外10%的数据,n3  a,b,c4  T5

    对于另外20%的数据,b=0

    对于另外10%的数据,T10

    对于全部100%的数据,a,b,c,n25  T5×105

    样例1

    样例2

     

    Solution:

    正解DP+组合数。(可能有多种方法,这里说其中的一种)我们发现,从正面看,横排放置的方块会互相影响(因为后面的可能比前面高,状态太复杂且不好转移),我们可以考虑纵向放置的方案数。纵列和纵列之间不会相互遮挡,因此方案数很好统计。

    所以我们需要处理出纵列合法的方案数。虽然有三种方块,但我们只是需要一种漏在外面,所以可以把另外两种先不考虑,把它们合成一个状态。
    处理任意一列合法方案数,可以dfs处理,也可以dpf[i][j][k][x][y]表示放置到第 i个格子,高度为 j,已经放置的最高高度为 k,已经用了 x个能看见的方块,y 个不能看见的方块的方案数边界条件:最高可以放到max(a,b,c),我们发现如果高度超过了所有种类颜色的数量,那么我们不可能使得这种情况合法。需要注意的是, y的最大值应当是2*max(a,b,c),因为我们把另外两种方块整合在一起了。那么就可以按顺序暴力的放置了。每个格子可能有两种操作,在它上面放一个新的格子,或者把下一个格子放到下一行去。如果下个位置的高度超过了枚举的最高高度,那么前面就没有格子可以遮挡它,它必须放能看见的种类,并更新最高高度。否则说明下个位置的高度不够高,会被前面已经放置的格子遮挡,那么放什么种类就都可以啦。

    状态转移方程:

    放到下一行:
    f[i+1][0][k][x][y]+=f[i][k][k][x][y];
    放到上面:
    if (j==k)
    f[i][j+1][k+1][x+1][y]+=f[i][j][k][x][y];
    else
    f[i][j+1][k][x+1][y]+=f[i][j][k][x][y],
    f[i][j+1][k][x][y+1]+=f[i][j][k][x][y];
    复杂度O(n5)

    为了方便统计,标程的 i计算到了n+1这样我们就可以处理出,因为i=n的所有情况都统计在了f[n+1][0]中。

    这样我们就可以处理出g[i][j]表示在某一列上,用了i 种能看见的方块,j 种遮挡住的方块的方案数。每一行的方案数处理出来了,我们就可以开始真正的dp了。

    dp[i][j][k]表示从左往右(正面)放到第i 列,用了j 种能看见的方块, k种遮挡的方块的方案数。dp[i+1][j+x][y+k]+=dp[i][j][k]*g[x][y];

    复杂度O(n5)

    处理出dp数组之后,实际上我们就得到了答案的一部分。如果只有两种方块可供放置,那么dp数组就是答案了就是我先前说的b=0的部分。

    但是我们有三种方块。考虑让一种方块漏出来,那么另外两种方块的位置是可以随意组合的(因为并不会漏在外面)。所以还需要处理一下组合数。数据范围很小,可以直接预处理。如果只让一种(如住宅楼)能看见,那么方案数已经显而易见了。

    dp[n][a][b+c]*C[c+b][b];

    dp表示使得a 漏在外面,b,c遮住的方案数。C 数组是组合数,表示一共有b+c个位置,b、c随意组合的方案数。

    那么最终答案就呼之欲出了。

    ans=(dp[n][a][b+c]*C[b+c][b])+(dp[n][b][c+a]*C[c+a][c])+(dp[n][c][a+b]*C[a+b][a]); 

    我们发现其实对于同一个n ,处理出来的dp数组是完全一致的,我们可以将a,b,c处理到范围的最大值25,就可以O(1)查询得到对于不同a,b,c的答案了。
    时间复杂度O(2*n5+常数),完全是可以过的(除非评测鸡太差)。 

    输入数据很大,为了尽量减少常数的影响,建议使用快速读入。不过经过测试, scanf 理论是可以通过的。此题没有刻卡常,各位可以随意踩std。

    代码:

     1 #include<bits/stdc++.h>
     2 #define mod 1000000007
     3 #define N 27
     4 using namespace std;
     5 inline int read()
     6 {
     7     int x=0,f=1;char ch=getchar();
     8     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     9     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    10     return x*f;
    11 }
    12 int f[N][N][N][N][N<<1],g[N][N<<1],C[N<<1][N<<1],dp[N][N][N<<1];
    13 int ans,a,b,c,n,m,T;
    14 inline void add(int &x,int y){x+=y;if (x>=mod) x-=mod;}
    15 main()
    16 {
    17     n=read(),T=read();
    18     m=25;
    19     f[0][0][0][0][0]=1;
    20     for (int i=0;i<n;i++)
    21         for (int j=0;j<=m;j++)
    22             for (int k=j;k<=m;k++)
    23                 for (int x=k;x<=m;x++)
    24                     for (int y=0;y<=(m<<1);y++)
    25                         if (f[i][j][k][x][y])
    26                         {
    27                             add(f[i+1][0][k][x][y],f[i][j][k][x][y]);
    28                             if (j==k)
    29                                 add(f[i][j+1][k+1][x+1][y],f[i][j][k][x][y]);
    30                             else
    31                                 add(f[i][j+1][k][x+1][y],f[i][j][k][x][y]),
    32                                 add(f[i][j+1][k][x][y+1],f[i][j][k][x][y]);
    33                         }
    34     for (int k=0;k<=m;k++)
    35         for (int x=k;x<=m;x++)
    36             for (int y=0;y<=(m<<1);y++)
    37                 add(g[x][y],f[n][0][k][x][y]);
    38     C[0][0]=1;
    39     for(int i=1;i<=(m<<1);i++)
    40     {
    41         C[i][0]=1;
    42         for(int j=1;j<=i;j++)
    43         {
    44             C[i][j]=C[i-1][j-1]+C[i-1][j];
    45             if (C[i][j]>=mod) C[i][j]-=mod;
    46         }
    47     }
    48     dp[0][0][0]=1;
    49     for (int i=0;i<n;i++)
    50         for (int j=0;j<=m;j++)
    51             for (int k=0;k<=(m<<1);k++)
    52                 if (dp[i][j][k])
    53                     for(int x=0;j+x<=m;x++)
    54                         for(int y=0;y+k<=(m<<1);y++)
    55                             add(dp[i+1][j+x][y+k],(1ll*dp[i][j][k]*g[x][y])%mod);
    56     while(T--)
    57     {
    58         a=read(),b=read(),c=read();
    59         ans=0;
    60         add(ans,(1ll*dp[n][a][b+c]*C[b+c][b])%mod);
    61         add(ans,(1ll*dp[n][b][c+a]*C[c+a][c])%mod);
    62         add(ans,(1ll*dp[n][c][a+b]*C[a+b][a])%mod);
    63         printf("%d
    ",ans);    
    64     }
    65 }

  • 相关阅读:
    对软件工程课程的期望
    自我介绍
    新目标
    课后作业-阅读任务-阅读笔记-4
    20150914-构建之法:现代软件工程-阅读笔记
    团队编程项目作业2-团队编程项目开发环境搭建过程
    团队编程项目作业2-团队编程项目设计文档
    个人-GIT使用方法
    结对编程项目作业2-结对编项目设计文档
    结对编程项目作业2-开发环境搭建过程
  • 原文地址:https://www.cnblogs.com/five20/p/7857073.html
Copyright © 2011-2022 走看看