题面

有R红色立方体,G绿色立方体和B蓝色立方体。每个立方体的边长是1。现在有一个N × N的木板,该板被划分成1×1个单元。现在要把所有的R+G+B个立方体都放在木板上。立方体必须放置在单元格内,单元格可以竖立放置多个立方体。放置在板上的立方体可以被视为“建筑物”。一个“建筑物”被称为“美丽建筑物”,当且仅当:人站在南面,向北面望过去,观察建筑物时,所有可见立方体都是相同的颜色。例如,在下图中,左侧建筑物是“美丽建筑物”,而右侧建筑物则不是。问题是:给出R,G,B,N,有多少种不同的“美丽建筑物”,答案模1000000007。输入:
多组测试数据。第一行,一个整数group。表示有group组测试数据。1 <= group <= 8每组测试数据格式:一行,4个整数:R,G,B,N。  0<=R,G,B<26。 1 <= N <26。输出:
共group行,每行一个整数。

样例输入:

41  0  1  21  1  2  12  2  1  30  0  10  12

样例输出:

40162372185933

思路:DP

我们对于每一个颜色做为看得见的颜色做一次dp求解,最后再将答案统计起来就可以了。

不妨设看见的是Red(R),被挡住的是Gueen(G)和Blue(B)

那么我们从南向北看每一列都是独立的,所以就可以把大问题化为子问题。

那么可以设出dp方程:

f[i][x][y][z]表示前i列用了x个R,y个G,z个B的方案数
f[i][x][y][z]+=f[i-1][x-a][y-b][z-c]*(这一列用了a个R,b个G,c个B的方案数)

那么就解决下一个问题: 如何计算 这一列用了a个R,b个G,c个B的方案数。

那么我们设

g[x][y][z]表示一列使用x个R,y个G,z个B的方案数。

计算一列满足条件的方案数时还要考虑高度,很明显,我们每一列能看到的R颜色数量就是这一列的最高高度。所以
我们又设:

s[i][x][y][z][h]表示一列内,前i行,使用x个R,y个G,z个B,最高高度为h的方案数

那么g数组就是:

i表示枚举的最高高度
g[x][y][z]+=s[n][x][y][z][i];
s[i][x][y][z][i]表示到第n行最高高度是i,使用x个R,y个G,z个B,最高高度为h的方案数。
将他们全部相加就是g数组的值。

S数组的转移比较复杂一点,分两种情况:

1、这一行不是最高高度

2、这一行变成了最高高度

那么对于第一种情况:枚举自身的高度,用R颜色数量,G颜色数量,算出B颜色数量,然后进行转移

对于第二种情况:枚举之前的最高高度,用R颜色数量,G颜色数量,算出B颜色数量,然后进行转移。

第二种情况相比于第一种要多出一个条件,就是多出来的高度必须是R颜色的,不然就不满足方程性质。

关于S数组的转移方式,枚举出来的方式只是使用的数量,所以还有计算出这些方块的排列方式,即可重复排列,计算公式:是总数的阶乘除以各种颜色数量的阶乘,在这道题为

n=x+y+z
n!/x!/y!/z!

计算可重集排列可以使用逆元相关知识,或者暴力预处理也可以。

S数组转移:

a表示用R颜色数量,b表示用G颜色数量,c表示用B颜色数量
第一种:
s[n][x][y][z][h]+=s[n-1][x-a][y-b][z-c][h]*可重复排列(a,b,c)
i表示枚举的之前最高高度
第二种:
s[n][x][y][z][h]+=s[n-1][x-a][y-b][z-c][i]*可重复排列(a-(h-i),b,c)
a-(h-i)是因为有a-(h-i)个R颜色是必须选的,不能掉换位置。

边界问题:下面代码有注释

那么当S,G,F数组都处理完了就可以直接累加答案了。时间复杂度:O(n^8)

写法

关于这道题的写法,推荐使用记忆化搜索式写法,比较简单易懂,并且我这种写法转换成O(n^6)会写起来比较方便,所以推荐写成记忆化搜索形式。

如果还有什么不明白的可以详见代码注释,下面已经标明了每一步的作用。

//smoj2806 建筑物 O(n^8) 暴力写法40分
#include<bits/stdc++.h>
#define mod 1000000007
#define int long long
#define maxn 10001
using namespace std;
int mul_inv[maxn],mul[maxn],inv[maxn],R,G,B,n;
int S[26][26][26][26][26],F[30][30][30][30],g[30][30][30],T;
long long Rearrangeable(int x,int y,int z){             //计算可重集:n!/x!/y!/z!int n=x+y+z;return ((((mul_inv[x]*mul_inv[y])%mod)*mul_inv[z])%mod)*mul[n]%mod; //使用逆元计算
}
int s(int i,int x,int y,int z,int h){                   //计算S数组long long sum=0;if(i==1){           //边界if(x==h&&(y+z)==0)return S[i][x][y][z][h]=1;    //如果剩下的全是x颜色和第一行最高高度是x的个数就说明可以return S[i][x][y][z][h]=0;              //不然这种方案就不行}if(S[i][x][y][z][h]!=-1){return S[i][x][y][z][h]%mod;}  //记忆化for(int h2=0;h2<=h;h2++){           //my height,枚举自己这一行的高度for(int a=0;a<=x&&a<=h2;a++){           //枚举用了几个x颜色for(int b=0;b<=y&&b+a<=h2;b++){     //枚举用了几个y颜色int c=h2-a-b;                   //计算用了几个z颜色if(c<=z)        //用的z颜色个数要需要小于z的总个数sum+=s(i-1,x-a,y-b,z-c,h)%mod   //搜索下去累加答案*Rearrangeable(a,b,c)%mod;  //乘上可重集sum%=mod;}}}for(int h2=0;h2<h;h2++){            //those max height,枚举自己前面的最高高度for(int a=0;a<=x&&a<=h;a++){for(int b=0;b<=y&&a+b<=h;b++){int c=h-a-b,a1=h-h2;            //a1表示自己超过之前最高高度几个if(a1<=a&&c<=z)         //判断满不足满足条件 : 1、必须用的a1<=选择用的a  2、同上my heightsum+=s(i-1,x-a,y-b,z-c,h2)%mod*Rearrangeable(a-a1,b,c)%mod;   //可重集能选的少了a1sum%=mod;}}}S[i][x][y][z][h]=sum%mod;return S[i][x][y][z][h]%mod;        //返回
}
int getG(int x,int y,int z){                            //计算g数组long long sum=0;if(g[x][y][z]!=-1)return g[x][y][z]%mod;            //记忆化for(int i=0;i<=x;i++)sum+=s(n,x,y,z,i)%mod,sum%=mod;//累加全部高度的S数组g[x][y][z]=sum%mod;return g[x][y][z]%mod;              //返回
}
int f(int i,int x,int y,int z){                         //计算F数组long long sum=0;if(i==1){return getG(x,y,z)%mod;}   //边界,第一列就把全部放完if(F[i][x][y][z]!=-1)return F[i][x][y][z]%mod;      //记忆化for(int a=0;a<=x;a++){for(int b=0;b<=y;b++){for(int c=0;c<=z;c++){sum+=f(i-1,x-a,y-b,z-c)%mod             //往下搜*getG(a,b,c)%mod;                   //乘上那一列的方案数sum%=mod;}}}F[i][x][y][z]=sum%mod;return F[i][x][y][z];               //返回
}
signed main(){freopen("2806.in","r",stdin);freopen("2806.out","w",stdout);memset(S,-1,sizeof(S));mul[0]=1;mul_inv[0]=1;inv[1]=1;     //以下4行计算逆元、阶乘、阶乘逆元for(int i=1;i<=100;i++)mul[i]=mul[i-1]*i%mod;for(int i=2;i<=100;i++)inv[i]=(mod-mod/i)%mod*inv[mod%i]%mod;for(int i=1;i<=100;i++)mul_inv[i]=mul_inv[i-1]%mod*inv[i]%mod;scanf("%lld",&T);while(T--){memset(F,-1,sizeof(F));memset(g,-1,sizeof(g));long long ans=0;scanf("%lld%lld%lld%lld",&R,&G,&B,&n);ans+=f(n,R,G,B);ans%=mod;ans+=f(n,G,R,B);ans+=f(n,B,R,G);   //累加以3种不同颜色为可见颜色的方案printf("%lld\n",ans%mod);       //输出}return 0;
}

当你理解O(n^8)时,你就可以轻松理解O(n^6)的思路。

第一层优化 O(N^6)

其实我们在转移的dp方程的时候可以发现,被挡住的那G,B两个颜色几乎没有什么区别,他们不会影响方程的性质,只要R颜色能挡住他们,他们就不会影响一个方案的正确性。

那么我们可以思考,可不可以将G和B两种颜色混合成一种颜色来降低复杂度呢?

当然可以!将两种颜色混合成一种颜色后可以将复杂度降为O(N^6)的,大大提高效率。

但问题来了,如果将两种混合成一种,计算可重复排列会出偏差,江来是要付泽任的,你民不民白!

那么怎么解决呢?我们从全局来想,如果我们已经构成了一个合理的方案。那么现在,我们把全部的G和B都变为Y(Yellow),那这一样是看过去只有R的,我们想,在这全部的Y里,有x个是G,y
个是B,那么我们从Y中取x个染成G,其余染成B,那么不就是原来的其中一种方案嘛!

所以每次计算最后成上C(G+B,G)或C(G+B,B),两者是一样的。

所以我们把3种颜色变为2种颜色,答案最后乘上一个组合数就可以了。时间复杂度:O(N^6),足矣!

写法

其实相比于O(N^8)的写法,O(N^6)的并没有多大的改造,我们可以将记忆化搜索刚开始传进去的

f(n,R,G,B)

改为

f(n,R,G+B,0)

就可以轻松实现O(N^8)到O(N^6)的飞跃。
具体实现看代码,原来O(N^8)的注释就不标了

//smoj2806 建筑物 O(n^6) 100分
#include<bits/stdc++.h>
#define mod 1000000007
#define int long long
#define maxn 10001
using namespace std;
int mul_inv[maxn],mul[maxn],inv[maxn],R,G,B,n;
int S[26][26][5][51][26],F[30][30][30][60],g[30][30][60],T;
long long Rearrangeable(int x,int y,int z){int n=x+y+z;return ((((mul_inv[x]%mod*mul_inv[y]%mod)%mod)*mul_inv[z]%mod)%mod)*mul[n]%mod;
}
int C(int n,int m){     //组合数计算return (mul[n]%mod*((mul_inv[n-m]%mod*mul_inv[m]%mod)%mod))%mod;    //还是用逆元
}
int s(int i,int x,int y,int z,int h){       //没变long long sum=0;if(i==1){if(x==h&&(y+z)==0)return S[i][x][y][z][h]=1;return S[i][x][y][z][h]=0;}if(S[i][x][y][z][h]!=-1){return S[i][x][y][z][h]%mod;}for(int h2=0;h2<=h;h2++){           //my heightfor(int a=0;a<=x&&a<=h2;a++){for(int b=0;b<=y&&b+a<=h2;b++){int c=h2-a-b;if(c<=z)sum+=s(i-1,x-a,y-b,z-c,h)%mod*Rearrangeable(a,b,c)%mod;sum%=mod;}}}for(int h2=0;h2<h;h2++){            //those max heightfor(int a=0;a<=x&&a<=h;a++){for(int b=0;b<=y&&a+b<=h;b++){int c=h-a-b,a1=h-h2;if(a1<=a&&c<=z)sum+=s(i-1,x-a,y-b,z-c,h2)%mod*Rearrangeable(a-a1,b,c)%mod;sum%=mod;}}}S[i][x][y][z][h]=sum%mod;return S[i][x][y][z][h]%mod;
}
int getG(int x,int y,int z){            //没变long long sum=0;if(g[x][y][z]!=-1)return g[x][y][z]%mod;for(int i=0;i<=x;i++)sum+=s(n,x,y,z,i)%mod,sum%=mod;g[x][y][z]=sum%mod;return g[x][y][z]%mod;
}
int f(int i,int x,int y,int z){         //没变long long sum=0;if(i==1){return getG(x,y,z)%mod;}if(F[i][x][y][z]!=-1)return F[i][x][y][z]%mod;for(int a=0;a<=x;a++){for(int b=0;b<=y;b++){for(int c=0;c<=z;c++){sum+=f(i-1,x-a,y-b,z-c)%mod*getG(a,b,c)%mod;sum%=mod;}}}F[i][x][y][z]=sum%mod;return F[i][x][y][z];
}
signed main(){freopen("2806.in","r",stdin);freopen("2806.out","w",stdout);memset(S,-1,sizeof(S));memset(F,-1,sizeof(F));memset(g,-1,sizeof(g));mul[0]=1;mul_inv[0]=1;inv[1]=1;for(int i=1;i<=100;i++)mul[i]=mul[i-1]*i%mod;for(int i=2;i<=100;i++)inv[i]=(mod-mod/i)%mod*inv[mod%i]%mod;for(int i=1;i<=100;i++)mul_inv[i]=mul_inv[i-1]%mod*inv[i]%mod;scanf("%lld",&T);while(T--){//memset(S,-1,sizeof(S));memset(F,-1,sizeof(F));memset(g,-1,sizeof(g));long long ans=0;scanf("%lld%lld%lld%lld",&R,&G,&B,&n);ans+=f(n,R,0,G+B)%mod*C(G+B,B)%mod; ans%=mod;       //将R作为可见颜色ans+=f(n,G,0,R+B)%mod*C(R+B,B)%mod; ans%=mod;       //将G作为可见颜色ans+=f(n,B,0,R+G)%mod*C(R+G,R)%mod; ans%=mod;       //将B作为可见颜色printf("%lld\n",ans%mod);}return 0;
}

那么这个复杂度就可以顺利地通过此题了,当然,还有下一层优化,可以进一步优化成O(N^5)的,在这里就先不说了,大家自己下去好好想想。

谢谢观赏!

转载于:https://www.cnblogs.com/hyfhaha/p/10678142.html

smoj2806建筑物相关推荐

  1. 建筑物占据的网格数目的确定(三)

    2019独角兽企业重金招聘Python工程师标准>>> 前面讲了如何处理拼接菱形的冲突问题, 一个建筑物的底座就是一个拼接菱形,如何得到一个图形的底座呢? 一个有底座的图形的特点是什 ...

  2. 形态学边缘提取matlab,在Matlab平台下基于形态学方法对LIDAR数据进行建筑物边缘提取...

    1引言机载LIDAR系统能够直接获取地面三维数据,具有高精度.高密度.高效率和成本低等优点,在现代测绘中发挥了越来越重要的角色,如512大地震中,此系统在震后搜救工作中就发挥了重要作用.但是LIDAR ...

  3. 天池赛题解析:零基础入门语义分割-地表建筑物识别-CV语义分割实战(附部分代码)

    赛题内容 赛题背景 赛题以计算机视觉为背景,要求选手使用给定的航拍图像训练模型并完成地表建筑物识别任务.为更好的引导大家入门,我们为本赛题定制了学习方案和学习任务,具体包括语义分割的模型和具体的应用案 ...

  4. MAT之SVM/BP:SVR(better)和BP两种方法比较且实现建筑物钢筋混凝土抗压强度预测

    MAT之SVM/BP:SVR(better)和BP两种方法比较且实现建筑物钢筋混凝土抗压强度预测 目录 输出结果 代码设计 输出结果 代码设计 load concrete_data.mat n = r ...

  5. (四)建筑物多边形化简系统——“去尾巴”和分割复杂多边形

    问题说明 实际操作中,发现有的多边形存在"尾巴"或者很细的部分."尾巴"细长,明显不是有效建筑物区域,特点就是区域面积小,看起来细长,附着于大面积多边形外测或者 ...

  6. html5 建筑物模型,基于HTML5的建筑物阴影实时模拟

    摘要: 随着HTML5的发布,Web GIS的发展有了新的机遇,它可以摒弃Flex,Silverlight等插件,HTML5以无插件的形式提供很多了API可以使Web GIS具有较强的交互功能,良好的 ...

  7. 【天池赛事】零基础入门语义分割-地表建筑物识别 Task6:分割模型模型集成

    [天池赛事]零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline(3 天) – 学习主题:理解赛题内容解题流程 – 学习内容:赛题理解.数据读取.比赛 baseline 构建 ...

  8. 【天池赛事】零基础入门语义分割-地表建筑物识别 Task5:模型训练与验证

    [天池赛事]零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline(3 天) – 学习主题:理解赛题内容解题流程 – 学习内容:赛题理解.数据读取.比赛 baseline 构建 ...

  9. 【天池赛事】零基础入门语义分割-地表建筑物识别 Task4:评价函数与损失函数

    [天池赛事]零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline(3 天) – 学习主题:理解赛题内容解题流程 – 学习内容:赛题理解.数据读取.比赛 baseline 构建 ...

最新文章

  1. java.lang.OutOfMemoryError及解决方案
  2. 4.Python算法之试探算法思想(回溯法)
  3. ServiceStack.Text反序列化lowercase_underscore_names格式的JSON
  4. 2016 10 26考试 NOIP模拟赛 杂题
  5. 【dfs】【拓扑排序】组合树
  6. 一分二功率分配器_一文学会微波功率分配器
  7. C#如何去掉字符串中所有空格
  8. oracle如何获取自增id,oracle实现自增id
  9. Python3.6下安装扩展库pywin32的正确姿势
  10. 东南大学计算机网络_【20考研】东南大学计算机考研分数统计
  11. 2021全国大学生计算机系统能力大赛操作系统设计赛第一场研讨会隆重举行
  12. C++新经典——C++从入门到精通
  13. CISCO 路由器(1)
  14. 多智能体通信:MAGNet用于深度多智能体强化学习的多智能体图网络
  15. 有限元分析法matlab,用MATLAB进行结构的有限元法分析
  16. CAM是利用计算机,利用DroidCam将手机摄像头打造成计算机摄像头
  17. 可汗学院:宏观经济学笔记
  18. 【React】状态管理仓库 原生\封装\第三方
  19. ssms 导出mysql_怎样使用SSMS管理器导出数据表 用SSMS管理器导出数据表的方法
  20. matlab中的for应用,Matlab中matlab中for_循环的原理和应用

热门文章

  1. redhat linux vim文本编辑工具的笔记
  2. clover直接进windows_设置Clover默认进入Windows,按快捷键F8可选择不同的引导
  3. 怎么介绍自己做过的项目呢?
  4. vuex五个属性及其使用传参
  5. java毕业设计 Vue 房屋出租出售管理系统(含源码+论文)
  6. 基于Leaflet的轨迹模拟回放
  7. 如果用户希望将自己计算机中的照片,信息技术会考模拟题共31套的选择三
  8. 阿里云免费版ssl 申请
  9. 古诗文,想要背得正确写得漂亮学得明白?买它!
  10. 4月29号 马上五一放假啦