UVA 12563 Jin Ge Jin Qu hao DP

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

  题目描述: 一共n首歌儿, 要求在t时间唱尽可能多的歌儿, 相同的歌儿的数量下总时长尽可能的长  

  解题思路: 设dp[i] 为正好唱i秒时唱的最多的歌儿的数量, 只有当i正好等于t[j]或者dp[i-t[j]]>=1 的时候才转移(因为i是正好唱了多少秒)

  代码: 

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <map>
#include <cstring>
#include <iterator>
#include <cmath>
#include <algorithm>
#include <stack>
#include <deque>
#include <map>

#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1

const int maxn = 1e5;
using namespace std;

int dp[maxn]; // dp(i, j) 表示前 i 首歌儿正好唱 j s
int t[100];

int main() {
    int T;
    scanf( "%d", &T );
    for( int c = 1; c <= T; c++ ) {
        int n;
        int tot;
        scanf( "%d%d", &n, &tot );
        for( int i = 1; i <= n; i++ ) {
            scanf( "%d", &t[i] );
        }
        tot--;
        memset(dp, 0, sizeof(dp) );
        int res = 0;
        for( int i = 1; i <= n; i++ ) {
            for( int j = tot; j >= t[i]; j-- ) {
//                if( i == 2 && j == 99 ) {
//                    cout << dp[1][30] << endl;
//                    
//                }
                if( dp[j-t[i]] >= 1 || j == t[i] ) {
                    dp[j] = max( dp[j], dp[j-t[i]]+1 );
                    res = max( res, dp[j] );
                }
            }
        }
        int i;
        for(i = tot; dp[i] != res; i--);
        if(res == 0)
            printf("Case %d: %d %d
", c, 1, 678);
        else
            printf("Case %d: %d %d
", c, 1 + res, i + 678);
    }
    return 0;
}
View Code

  思考: 一开始设计的状态是前 i 首歌儿 j 秒内唱的最多的数量, 后来发现这个只能把歌儿的数量求出来, 很难求最长时间, 然后我想既然是要求最长时间, 那我就改成前i 首歌儿 j 秒正好唱完的最多的数量, 发现状态没有办法转移, 因为dp[i][j] 没办法转移到 dp[k][j-t[i]] k是之前唱过的那首歌儿, 那么这样我就不关心前i首歌儿, 只关心正好唱了多少秒, 运用滚动数组就可以了......自己还是没办法将状态和题目的已知条件很好的联系起来......还是得经常做......

   生日快乐

原文地址:https://www.cnblogs.com/FriskyPuppy/p/7289867.html