Count Increasing Two-Row Tables
Autumn Tournament (2015)
Submission name: table.cpp
Problem summary
Place1…2N once each into a2×N table, increasing along every row and column. Values in set A must be on top; values in B must be on the bottom; all other values are unrestricted. Count valid tables.
Input
N; then |A| and its values; then |B| and its values.
Output
The number of valid tables.
Constraints
2≤N≤35; A and B are disjoint, each of size at mostN.
Worked example
Original statement sample.
Input
4 2 3 2 3 4 8 7
Output
2
Solution analysis
- Process numbers1,2,…,2N in increasing order and decide only their row. Their order within a row is then forced. At every prefix, the number placed on top must be at least the number on the bottom: otherwise some bottom entry would have no smaller entry above it.
- Let dp[top] count choices after processing a prefix; bottom=processed−top. Put the next value on top if it is not forced into B and top<N. Put it below if it is not forced into A and bottom<top. This last inequality keeps every column increasing.
- After all2N numbers, read dp[N]. In the sample, the two top rows are1,2,3,5 and1,2,3,6; their complementary bottom rows are4,6,7,8 and4,5,7,8. Both obey the forced row choices.
Why it works
Choosing a row for every value gives exactly one row-sorted table. Its columns increase precisely when every processed prefix has at least as many top entries as bottom entries: the j-th bottom entry must appear after the j-th top entry. The DP enforces exactly this condition and all forced choices, and its alternatives are disjoint.
Watch out. A forced-bottom value may be impossible early in the sequence, giving zero ways. Do not multiply by row permutations: increasing rows determine their order uniquely.
Commented C++17 solution
An independently written complete program with standard headers.
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n;
cin>>n;
vector<int>forced(2*n+1,0);
for(int row=1;row<=2;row++){
int count;
cin>>count;
while(count--){
int value;
cin>>value;
forced[value]=row;
}
}
vector<long long>ways(n+1,0);
ways[0]=1;
for(int value=1;value<=2*n;value++){
vector<long long>next(n+1,0);
for(int top=0;top<=n;top++){
int bottom=value-1-top;
if(bottom<0||bottom>top){
continue;
}
if(forced[value]!=2&&top<n){
next[top+1]+=ways[top];
}
// A bottom cell needs an already placed smaller top cell.
if(forced[value]!=1&&bottom<top){
next[top]+=ways[top];
}
}
ways.swap(next);
}
cout<<ways[n]<<'\n';
}