题意:
给出一些各有一定数量的不同颜色的砖块,现在要将这些砖块堆成一些塔,要求:
所有颜色相同的砖块必须在同一层;
定义两种不同的方案为:存在一个颜色对
i,j
i
,
j
满足在A方案中,有颜色
i
i
在颜色的砖块上的情况,而在B中不存在。
现在需要移除这些砖块,移除的规则如下:
每次移除,声明一种颜色,将表面的所有这种颜色的砖块全部移除,并且每种颜色只能声明一次。要求最后必须移除所有砖块。
现在要求使得移除的方案数在
MINways,MAXways
M
I
N
w
a
y
s
,
M
A
X
w
a
y
s
两个给出的常数之间。
求能满足这个条件的摆放砖块的方案数。
颜色总数
N≤6
N
≤
6
分析:
题意很复杂,不过还是比较容易理解。
忽略N的大小,很容易想到一些神奇的构造方案的算法,然而N的范围已经决定了,这题绝对没那么难。
由于N的范围极小,我们可以尝试暴力,用 O(nn×2x) O ( n n × 2 x ) 的复杂度暴力枚举每一个摆放方案(即枚举每个方块在某层,再将相邻层数的暴力枚举是否覆盖,易知x的最大值为9)
当然,枚举出来后,由于数量上的限制,我们不确定这个方案是否合法。所以我们需要检验这个方案。。。然后,这道题最神奇的一点来了,在已经有 O(nn×2x) O ( n n × 2 x ) 复杂度的情况下,再套一个算法,估计没人会大胆到用网络流。。。然而的确用网络流是比较优秀的一种方式。
我们将每两层之间分别构图,
将每个点拆分为两个,分别处理放于其上,以及放于其下的情况,中间的边下限为1,上限为正无穷,右侧的边下限与上限均为其个数。左边的边无下限。
然后按照带上下界的网络流的基本方法:将图构造为
然后检验是否满流即可。
现在我们完成了摆放方案以及检验,需要求出移除的方案数,这时候再暴力枚举一个排列即可。
总的复杂度为
O(nn×2x×((2n)3+n!))
O
(
n
n
×
2
x
×
(
(
2
n
)
3
+
n
!
)
)
说实话我也不知道为什么能跑过。。。。当然,裸跑的确也是跑不过的。
所以需要加一些优化。
我这里推荐一个优化是:在完成枚举摆放方案后,检查是否当前颜色能覆盖的所有颜色的砖块总数是否小于当前颜色砖块数。这个优化效果实测较优
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 30
#define INF 0x3FFFFFFF
using namespace std;
int w[MAXN][MAXN],n,m,s,s1,t,t1,r[MAXN],ans,d[MAXN],l[MAXN][MAXN],minw,maxw;
bool used[MAXN];
int dx[MAXN],g[MAXN];
int getb(int x){
if(x==n+1)
return 1;
int res=0;
for(int i=1;i<=n;i++)
if(used[i]==0){
int j=1;
for(;j<=n;j++)
if(l[i][j]&&used[j]==0)
break;
if(j>n){
used[i]=1;
res+=getb(x+1);
used[i]=0;
}
}
return res;
}
int dfsx(int x,int maxf){
if(x==t)
return maxf;
for(int i=1;i<=t;i++)
if(w[x][i]!=0&&dx[i]+1==dx[x]){
int f=dfsx(i,min(w[x][i],maxf));
w[x][i]-=f;
w[i][x]+=f;
return f;
}
g[dx[x]]--;
if(g[dx[x]]==0){
dx[s]=m;
return 0;
}
int mind=m;
for(int i=1;i<=t;i++)
if(w[x][i]!=0)
mind=min(mind,dx[i]);
dx[x]=mind+1;
g[dx[x]]++;
return 0;
}
int solve(){
int res=0;
g[0]=m;
memset(dx,0,sizeof dx);
//PF("[");
while(dx[s]<m){
res+=dfsx(s,INF);
}
//PF("]");
return res;
}
bool check(){
int ws=getb(1);
if(ws<minw||ws>maxw)
return 0;
s1=2*n+1;
s=2*n+2;
t1=2*n+3;
t=2*n+4;
int res=0;
memset(w,0,sizeof w);
for(int i=1;i<=n;i++){
w[n+i][t1]+=r[i];
if(d[i]==1)
continue;
w[s1][t]+=r[i];
w[s][i]+=r[i];
res+=r[i];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(l[i][j]==1){
w[s][i+n]++;
w[j][t]++;
w[j][i+n]=INF;
res++;
}
w[t1][s1]=INF;
/*if(d[1]==1&&d[2]==2&&d[3]==3&&l[1][2]==1&&l[2][3]==1){
PF("*[%d]*\n",res);
for(int i=1;i<=t;i++){
for(int j=1;j<=t;j++){
if(w[i][j]==INF)
PF("- ");
else
PF("%d ",w[i][j]);
}
PF("\n");
}
}*/
//PF("[%d %d]",res,solve());
return solve()==res;
}
bool check1(){
int maxi=0;
for(int i=1;i<=n;i++)
maxi=max(maxi,d[i]);
for(int i=1;i<=maxi;i++){
bool flag=0;
for(int j=1;j<=n;j++)
if(d[j]==i){
flag=1;
break;
}
if(!flag)
return 0;
}
return 1;
}
bool check2(){
for(int i=1;i<=n;i++){
if(d[i]==1)
continue;
int sum=0;
for(int j=1;j<=n;j++)
if(l[j][i]==1)
sum+=r[j];
if(sum<r[i])
return 0;
}
return 1;
}
void dfs1(int x,int y){
//PF("{%d %d}\n",x,y);
if(x==n+1){
if(check2())
if(check())
ans++;
return ;
}
if(d[x]==d[y]-1){
l[x][y]=1;
dfs1(x+(y/n),y%n+1);
l[x][y]=0;
}
dfs1(x+(y/n),y%n+1);
}
void dfs(int x){
if(x==n+1){
if(check1())
dfs1(1,1);
return ;
}
for(int i=1;i<=n;i++){
d[x]=i;
dfs(x+1);
}
}
class CosmicBlocks{
public:
int getNumOrders(vector<int> &a,int x,int y){
n=a.size();
m=2*n+4;
for(int i=1;i<=n;i++)
r[i]=a[i-1];
minw=x;
maxw=y;
dfs(1);
return ans;
}
};
/*int main(){
SF("%d",&n);
m=2*n+4;
for(int i=1;i<=n;i++)
SF("%d",&r[i]);
SF("%d%d",&minw,&maxw);
dfs(1);
PF("%d",ans);
}*/