Dorsey Thief

Difficulty Level : Easy-Medium

Required Knowledge : DP, Knapsack Problem

Problem Setter : Devendra Agarwal

Problem Tester : Cao Peng

Approach

Let us assume X is the maximum profit Mr. Dorsey can make by selling gold for i passengers.
Once this is done for calculating for i passengers, it can be extended to calculate for (i+1)th passenger. How?

Say the (i+1)th passenger offered Dorsey $v in exchange for ‘a’ grams of gold,then recalculate it by taking two possibilities , i.e. either to take the value offered by the (i+1)th passenger or to not take the value offered by the (i+1)th passenger So the formula becomes:

for ( j=a;j<=X;j++)
	dp[j]=max(dp[j],dp[j-a]+v)

And after Mr. Dorsey has enquired all the passengers ,then he can get to know his maximum profit by dp[X]

Time Complexity

The time complexity is given by NX here.
The time complexity can be further reduced.
If you notice that as max(X) ≤ 5000 , so you only need to store highest (5000/i) values for i gram gold. So ∑(5000/i) = 43376 and thus the worst time complexity reduces to 43376
X + Sorting Time

Setter’s Code


#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<string.h>
using namespace std;
#define INF (1<<30)
typedef long long int ll;
typedef struct Point Pt;
struct Point
{
	int v,wt;
	bool operator<(const Pt t)const
	{
		if(t.v<v)
			return 1;
		else
			return 0;
	}
}input[1000001];
vector<int> myvector[5001];		//max x is 5000
int size[5001];				//max x is 5000
ll dp[5001];				//max x is 5000
ll max(ll a,ll b)
{
	if(a>b)	return a;
	else return b;
}
int main()
{
	int N,x,y,value;
	scanf("%d%d",&N,&x);
	for(int i=0;i<N;i++)	scanf("%d%d",&input[i].v,&input[i].wt);
	sort(input,input+N);		//sorting by decreasing value.
	memset(size,0,sizeof(size));

	for(int i=0;i<N;i++){
		y=input[i].wt;
		if(y>x)	continue;
		else if(size[y]>x/y)	continue;			//stroing only the best x/y amounts for each y
		else{
			myvector[y].push_back(input[i].v);	
			size[y]++;
		}
	}

	//memset(dp,0,sizeof(dp));
	for(int i=1;i<=x;i++)	dp[i]=-(ll)1000000000*(ll)100000000;
	dp[0]=0;
	for(int i=1;i<=x;i++){
		while(!myvector[i].empty()){
			value=myvector[i].back();
			for(int j=x;j>=i;j--){
				if(dp[j-i]>=0)
				dp[j]=max(dp[j],dp[j-i]+(ll)value);	
			}
			myvector[i].pop_back();	
		}
	}
	if(dp[x]>=0)	printf("%lld\n",dp[x]);
	else	printf("Got caught!");
	return 0;
}

Tester’s Code

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <cassert>
#include <cstring>
#include <string>

using namespace std;


long long dp[5002];
vector<int> have[5002];

void better(long long &x,long long y,long long z) {
    if ((y >= 0) && (z >= 0) && (y + z > x)) {
        x = y + z;
    }
}

int main() {
int i,j,k,m,n,x,y;
    /* Enter your code here. Read input from STDIN. Print output to STDOUT */   
    scanf("%d%d",&n,&m);
    assert(n >= 1 && n <= 1000000);
    assert(m >= 1 && m <= 5000);
    memset(dp,0xff,sizeof(dp));
    for(dp[0] = 0;n;--n) {
        scanf("%d%d",&x,&y);
        assert(x >= 1 && x <= 1000000);
        assert(y >= 1 && y <= 1000000);
        if (y == 0) {
            if (x > 0) {
                dp[0] += x;
            }
            continue;
        }
        if (y > m) {
            continue;
        }
        have[y].push_back(x);
       
    }
    for (i = 1; i <= m; ++i) {
        if (!have[i].empty()) {
            sort(have[i].begin(),have[i].end());
            for (j = min((int) have[i].size(), m / i); j ; --j) {
                for (k = m; k >= i; --k) {
                    better(dp[k], dp[k - i], have[i][have[i].size() - j]);
                }
            }
            
            
        }
        
    }
    if (dp[m] >= 0) {
        printf("%lld\n",dp[m]);
    }
    else {
        puts("Got caught!");
    }
    return 0;
}