UVA-624 CD (DP)

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=565

UVA-624 CD (DP)_第1张图片


题目大意:一张CD上有不超过20首歌,选取时间和不超过n分钟的歌曲,要求时间和最大,最后按原顺序输出每首歌曲的时间,及时间和。

依旧是一个01背包,不过需要输出路径(题目没说明时间和相同时该怎么办,看了样例认为是选取歌曲最多的),按升序排序后,状态转移是先达到的,但由于从时间短的歌曲开始转移,所以转移到的第一个一定是歌曲最多的(AC后看到别人谈到这题是Special Judge...)

最初用dp[i]记录时间和i是否能够凑成,cur[i]表示时间和为i时选的最后一个歌的下标,AC之后又想到可以将dp数组与cur数组合并,减少空间浪费

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

struct Track {
    int minu,index;

    bool operator < (const Track& a) const {
        return minu<a.minu;
    }
}tra[25],path[25];

int n,m,dp[2005],ans,cnt,tmp;//dp[i]表示时间和为i时选的最后一个歌的下标,dp[i]=-1表示目前无法凑成为i时间和

bool cmp(const Track& a,const Track& b) {
    return a.index>b.index;
}

int main() {
    while(scanf("%d%d",&n,&m)==2) {
        memset(dp,-1,sizeof(dp));
        dp[0]=0;
        ans=0;
        for(int i=0;i<m;++i) {
            scanf("%d",&tra[i].minu);
            tra[i].index=i;
        }
        sort(tra,tra+m);//按时间升序排序
        for(int i=0;i<m;++i) {
            for(int j=n;j>=tra[i].minu;--j) {
                if(dp[j-tra[i].minu]!=-1&&dp[j]==-1) {//如果dp[j-tra[i].minu]能凑成,且dp[j]不能凑成
                    dp[j]=i;
                    ans=max(ans,j);
                }
            }
        }
        tmp=ans;
        cnt=0;
        while(tmp!=0) {
            path[cnt++]=tra[dp[tmp]];
            tmp-=tra[dp[tmp]].minu;
        }
        sort(path,path+cnt,cmp);//路径按下标升序排序
        while(--cnt>=0)
            printf("%d ",path[cnt].minu);
        printf("sum:%d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(dp,uva)