USACO 100 DP Problem 'cownum' Analysis

by Md. Mahbubul Hasan

OCT05 CONTEST Analysis and Data

Problem 'cownum' Analysis

by Md. Mahbubul Hasan
The state of the DP is not that obvious.

While solving this problem by myself the first idea that came to mind is the 
obvious one, O(N^5) that is sliding window. We need 4 information in a state 
in this algorithm, [at which position are we (n)][number at n-1 th 
postion][number at n-2 th state][number at n-3 th state].

If we think a bit we can reduce this to O(N^4). In that case the state will be:
[at which position are we (n)][minimum of number at n-1 th and n-2 th
postion][minimum of number at n-2 and n-3 th position]. (Why it is a valid 
state 
information? - I am leaving this question as excercise for the readers)

But from here we cant see any obvious improvement. So we step back and return 
to drawing board. In worst case, N  = 1000. So it is quite obvious that 
the expected solution should be O(N^2). How can we represent a state by only 
two variables? One obvious variable is the position number. And what should be 
the other one? We must use the limit. Ok, lets start with this type of state:

[position][the highest limit of the ID].

And if we think a bit then we can realize that this will lead to O(N^2) 
solution. This is how it can be cracked down:

Say we will label the cows from rear. That means we are going to assign cow 
number from n'th cow.

[i][j] = number of ways we can label j cows by id 1 to i.

1st obvious choice is we can label j cows using id 1 to i-1. ([i-1][j])
2nd choice is we label last two cows by i. ([i-1][j-2])
3rd choice we can label last one i, and other j-1 less than i. ([i-1][j-1])
4th choice, we can label last one x (ranging from 1 to i-1) and second last 
one i. (sum x = 0 to i-2 cnt[x][j-2])

So our recurrence is:
[i][j] = [i-1][j] + [i-1][j-2] + [i-1][j-1] + sum (x = 0 to j-2) [i][j-2]

This leads to our desired O(N^2) algorithm. A sample code is given below which 
implements the above idea:

(Note that in our recurrance we still have a linear loop. To get rid of it i 
used another array scnt[][] that keeps information about the sum. There is 
certainly a way not using two different array. We can just simply switch our 
dp state to sum state. That means in the following code we could have just 
used scnt[][] to solve the problem!)

#include<stdio.h>

int i,j,N,M,cnt[1001][1001],scnt[1001][1001];

int main()
{
	freopen("cownum.in","r",stdin);
	freopen("cownum.out","w",stdout);

	scanf("%d%d",&N,&M);

/*Base Case*/
	for(i=1;i<=N;i++) cnt[0][i]=scnt[0][i]=0;
	for(i=0;i<=N;i++) {cnt[i][0]=1; scnt[i][0]=i+1;}
	for(i=1;i<=N;i++) {cnt[i][1]=i; scnt[i][1]=scnt[i-1][1]+i;}
/*Base Case*/

	for(j=2;j<=N;j++)
		for(i=1;i<=N;i++)
		{
			cnt[i][j]=(cnt[i-1][j]+cnt[i-1][j-2]+cnt[i-1][j-
1]+scnt[i-2][j-2])%M;
			scnt[i][j]=(scnt[i-1][j]+cnt[i][j])%M;
		}

	printf("%d\n",cnt[N][N]);

	return 0;
}