Acwing 271. 杨老师的照相排列
题意:
有n个数分别是从1到n,现在排成k排,每排分别有Ci个数,要求每排每列的都是从小到大,问有多少种方案
题解:
因为每行每列都是单调的,因此我们可以从小到大一次考虑1 ~ n的位置。在任意时刻,已经安排好的位置的数在每一行都是递增关系的(因为我们是从小到大的安排),每个数都插在队尾。我们用一个5元组来表示每行的人数,(a1,a2,a3,a4,a5),ai表示第i行已经安排好的人数,当此时考虑下一步(即再安排一个新人时),我们考虑所有满足如下条件的行号i:
- ai<Ci
- i=1或ai−1>aia_{i-1}>a_{i}ai−1>ai
从上往下考虑,如果当前行未满,并且当前行人数小于上一行。(如果上一行人数比本行小,那应该优先放在上一行,记住我们是从小到大放置,所以必须顺序放,不然无法保证单调性)
如果满足条件,就对该行进行转移,该行人数+1,加上上一个状态的方案。
我们不需要关心(a1,a2,a3,a4,a5)这些已经排好的具体方案,其描述的轮廓内的合影方案总数就构成一个子问题。
注意直接开数组会爆,使用动态开数组
代码:
#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\n",a,b);
typedef long long ll;
using namespace std;
//Fe~Jozky
const ll INF_ll=1e18;
const int INF_int=0x3f3f3f3f;
inline ll read(){ll s=0,w=1ll;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1ll;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10ll+((ch-'0')*1ll),ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
void rd_txt(){#ifdef ONLINE_JUDGE#elsefreopen("in.txt","r",stdin);#endif
}
int k;
int a[10];
int main()
{//rd_txt();while(cin>>k){if(k==0)break;a[1]=a[2]=a[3]=a[4]=a[5]=0;for(int i=1;i<=k;i++){cin>>a[i];}unsigned int f[a[1]+2][a[2]+2][a[3]+2][a[4]+2][a[5]+2];memset(f,0,sizeof(f));f[0][0][0][0][0]=1;for(int i1=0;i1<=a[1];i1++){for(int i2=0;i2<=a[2];i2++){for(int i3=0;i3<=a[3];i3++){for(int i4=0;i4<=a[4];i4++){for(int i5=0;i5<=a[5];i5++){unsigned int x=f[i1][i2][i3][i4][i5];if(i1<a[1])f[i1+1][i2][i3][i4][i5]+=x;if(i2<a[2]&&i2<i1)f[i1][i2+1][i3][i4][i5]+=x;if(i3<a[3]&&i3<i2)f[i1][i2][i3+1][i4][i5]+=x;if(i4<a[4]&&i4<i3)f[i1][i2][i3][i4+1][i5]+=x;if(i5<a[5]&&i5<i4) f[i1][i2][i3][i4][i5+1]+=x;}}}}}cout<<f[a[1]][a[2]][a[3]][a[4]][a[5]]<<endl;}return 0;}