在SPOJ上解决JUICE时这个解决方案有什么问题?

时间:2015-04-27 16:54:08

标签: c++ algorithm dynamic-programming

问题是:

杰瑞在有趣的游戏中失去了自己:水果忍者。 Fruit Ninja是一款iPhone和iPad游戏,玩家可以从屏幕底部切割水果,并通过一片切割两个以上的水果获得奖励。一旦水果被切割,它就会变成小块,不能再切割了。

经过数月的训练,他成为这场比赛的职业球员。实际上,他可以随时切断屏幕上的所有水果。杰里也有一个坏习惯,他不愿意为未来的切割留下一些成果。换句话说,在杰瑞削减水果之后,屏幕上的所有水果都会破裂,没有人离开。这就是为什么他所有的朋友都称他为Juice Extractor。

现在他只考虑奖金,当他削减两个以上的成果时,他可以获得一些奖励分数,与他当时切片的果实数量相同。例如,如果Jerry用一个切片切割4个水果,他可以从这个切片中获得4个分数。

杰瑞得到水果时间表后,他知道每一个水果的出现时间和消失的时间。他只能在出现的时间和消失的时间之间将水果切成碎片。他想知道他可以获得的最高奖励分数。

输入

有几个测试用例;输入的第一行包含一个整数T,表示测试用例的数量。 (T <= 200)

对于每个测试用例,第一行包含一个整数N,表示水果的总数。 (1 <= N <= 1000)

接下来的N行,每行描述一个水果。对于每一行,有两个整数Xi和Yi,其中Xi是果实的出现时间,Yi是该果实消失的时间。 (0 <= Xi <= Yi <= 1000000000)

输出

对于每个测试用例,输出一个整数,表示Jerry可能获得的最大分数。有关详细信息,请参阅示例。

实施例

输入: 1 10 1 10 2 11 3 12 4 13 13 14 14 15 13 19 20 22 21 23 22 24

输出: 案例#1:10

这是代码: 它根据开始时间对时间间隔进行排序.dp [i]说明了第i个水果出现的时间。我们将仅在出现水果时进行切割,因为它将涵盖所有情况

#include<iostream>
#include<vector>
#include<algorithm>
#include<memory.h>
using namespace std;
struct node
{
    int x,y;
};
int cmp(node a,node b)
{
    if(a.x!=b.x)
    return a.x<b.x;
    return a.y<b.y;
}
int main()
{
    int test;
    cin>>test;
    int t=0;
    int dp[1002];
    while(test--)
    {
        t++;
        int n;
        cin>>n;
        vector<node> v(n);
        for(int i=0;i<n;i++)
        {
            int a,b;
            cin>>a>>b;
            v[i].x=a;
            v[i].y=b;
        }
        sort(v.begin(),v.end(),cmp);
        memset(dp,0,sizeof(dp));
        int mx=0;
        for(int i=0;i<n;i++)
        {
            int id=100000,match=0;
            for(int j=0;j<=i;j++)
            {
                if(v[j].y>=v[i].x)
                {
                    id=min(id,j);
                    match++;
                }
            }
            for(int j=id;j<=i;j++)
            {
                if(match>2)
                dp[i+1]=max(dp[i+1],dp[j]+match);
                else
                dp[i+1]=max(dp[i+1],dp[j]);
                if(v[j].y>=v[i].x)
                match--;
            }

            dp[i+1]=max(dp[i+1],dp[i]);
            mx=max(mx,dp[i+1]);

        }
        cout<<"Case #"<<t<<": "<<mx<<"\n";
    }
    return 0;
}

1 个答案:

答案 0 :(得分:0)

具有相同的开始时间时,可能会出错。

我使用您的代码运行此测试用例:

  

10

     

1 1

     

1 2

     

3 3

     

1 4

     

3 5

     

3 6

     

3 7

     

8 8

     

3 9

     

3 10

您的代码输出 10 但答案实际上是 9

由于这个测试用例,我有几个WA, 我处理完这个案子后得到了AC ......我将分享我的经验,请看看你是否也有同样的错误。

使用此测试用例,我的代码将输出 10,我将快速解释如下:

让dp(i)成为您在第i个片段的开始时间最后一次剪辑的状态

显然,对于所有j <1,dp(i)=最大(dp(j)+段的数量(在j之后)与第i个段相交)。我

从物理角度来说,这意味着你在第j段的开始时间有第二次最后一次切割这是逻辑除了,如果第j段和第i段的开始时间是一样的!然后它不是2个不同的切割但是相同的切割!

要处理此问题,正确的方法是选择仅具有相同开始时间的连续段的最后一段!

假设片段已经按开始时间排序,下面的顺序是排序顺序

这是错误,我的程序(也许你的程序)给出输出10因为(使用0为基础)

DP(9)= DP(6)+ [7,9]

中的#重叠段

= 7 + 3 = 10!

如果你看得更深,DP(6)= 7实际上是正确的,因为它之后不会考虑这些段。但是你不应该使用DP(6)更新任何DP(X)的X> 6!,因为具有相同开始时间(第6段)的最后一段是8-细分

简而言之,如果我们使用某个DP状态而不是最后一个具有相同启动时间的DP状态来更新其他DP状态,则可能会出现问题。

我的解决方案是,当我在第i个段上执行DP时,每当我发现一些段j具有与段i(j 我将DP(j)设置为负无穷大,因此它不能足以更新其他状态。

已编辑:已接受的已添加代码 根据OP的要求,下面是我的代码

#include<bits/stdc++.h>
#define pii pair<int,int>
#define x first
#define y second
#define INF 1LL<<28
using namespace std;

int T,n;
int dp[1005];
pii a[1005];
int main(){
	scanf("%d", &T);
	for(int qwe=1; qwe <= T; qwe++){
		scanf("%d", &n);
		for(int i=0; i<n;i++) scanf("%d%d", &a[i].x, &a[i].y);
		sort(a, a+n);
		memset(dp,0,sizeof(dp));
		
		for(int i=0; i<n;i++){
			int cnt = 0;
			for(int j=i-1; j>=0; j--){
				if(a[j].x == a[i].x){dp[j] = -INF; cnt++; continue;}
				cnt += (a[i].x >= a[j+1].x && a[i].x <= a[j+1].y);
				dp[i] = max(dp[i], dp[j] + (cnt>2? cnt:0));
			}
			
			cnt += (a[i].x >= a[0].x && a[i].x <= a[0].y);
			dp[i] = max(dp[i], (cnt>2? cnt:0));
		}
		printf("Case #%d: %d\n", qwe, dp[n-1]);
	}
	return 0;	
}