Official

D - Money in Hand Editorial by en_translator


This problem can be solved with a DP (Dynamic Programming).

We define a two-dimensional array \(dp[i][j]\) \((0\leq i\leq N, 0\leq j\leq X)\) to be “True if it is possible to pay exactly \(j\) yen with \(B_1\) coins worth \(A_i\) yen, \(B_2\) coins worth \(A_2\) yen, \(\ldots\), and \(B_i\) coins worth \(A_i\) yen; and False otherwise.” We consider determining for \(i=0,1,\ldots,N\) in this order.
The answer to the problem is Yes if \(dp[N][X]\) is True, and No if it is False.

First, for \(i=0\), Takahashi has no coins, so \(dp[0][0]=\)True and \(dp[0][j]=\) False for \(j>0\).

Now suppose that we know \(dp[i-1][j]\) \((0\leq j\leq X)\). How can we find \(dp[i][j]\)?
We can use between \(0\) and \(B_i\) coins worth \(A_i\) yen, so \(dp[i][j]\) is True if there is at least one integer \(k\) satisfying \(0\leq k\leq B_i\) such that \(j-kA_i\geq 0\) and \(dp[i-1][j-kA_i]=\), and False otherwise. Formally,

\[ dp[i][j]=dp[i-1][j]\lor dp[i-1][j-A_i]\lor dp[i-1][j-2A_i]\lor \cdots\lor dp[i-1][j-B_iA_i]\quad (\text{where }dp[i-1][j']=\text{False}\text{ if }j'<0). \]

A naive implementation of this DP costs \(O(B_iX)\) for each \(i\), for a total of \(O((\displaystyle\sum_{i=1}^N B_i)X)\), which is fast enough under the Constraints of this problem. Therefore, the problem has been solved.

About \(O(NX)\) solution

This problem can also be solved in an \(O(NX)\) time. The fundamental idea is same as before, but when finding \(dp[i][j]\),
we compute \(dp'[i-1][j]=\) ( the maximum \(j'\) among \(j'=j,j-A_i,j-2A_i,\ldots\) such that \(dp[i-1][j']=\)True) (or \(-\infty\) if there is no such \(j'\)), for each \(j\).

Each \(dp'[i-1][j]\) can be filled in ascending order of \(j\), \(dp'[i-1][j]=j\) if \(dp[i-1][j]=\)True and \(dp'[i-1][j]=dp'[i-1][j-A_i]\), in an \(O(1)\) time each. What is more, by the recurrence relation that we derived in the solution above, \(dp[i][j]\) turns out to be True if \(dp'[i-1][j]\geq j-A_iB_i\) and False otherwise, which can be evaluated in an \(O(1)\) time as well.
Thus, the DP table is filled in an \(O(X)\) time for each \(i\), for a total of \(O(NX)\) time.

Sample code in C++ (base solution):

#include <bits/stdc++.h>
using namespace std;

int main(void){
	int n,x;
	int a[50];
	int b[50];
	bool dp[51][10001];

	cin>>n>>x;
	for(int i=0;i<n;i++)cin>>a[i]>>b[i];

	for(int i=0;i<=n;i++)for(int j=0;j<=x;j++)dp[i][j]=false;
	dp[0][0]=true;
	for(int i=0;i<n;i++){
		for(int j=0;j<=x;j++){
			for(int k=0;k<=b[i];k++){
				if(j>=a[i]*k){
					if(dp[i][j-a[i]*k])dp[i+1][j]=true;
				}
			}
		}
	}

	if(dp[n][x])cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	return 0;
}

Sample code in C++ (\(O(NX)\) solution):

#include <bits/stdc++.h>
using namespace std;

int main(void){
	int n,x;
	int a[50];
	int b[50];
	bool dp[10001]; //we reuse the array
	int mx[100];

	cin>>n>>x;
	for(int i=0;i<n;i++)cin>>a[i]>>b[i];

	for(int i=0;i<=x;i++)dp[i]=false;
	dp[0]=true;
	for(int i=0;i<n;i++){
		for(int j=0;j<a[i];j++)mx[j]=-10000; //-a[i]*b[i]未満なら良い
		for(int j=0;j<=x;j++){
			if(dp[j])mx[j%a[i]]=j;
			if(mx[j%a[i]]>=j-(a[i]*b[i]))dp[j]=true;
			else dp[j]=false;
		}
	}

	if(dp[x])cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	return 0;
}

posted:
last update: