几道简单dp题目。

两道DP后打印的:

一。劲歌金曲(Jin Ge Jin Qu [h]ao, Rujia Liu's Present 6, UVa 12563)

题目描述:

(If you smiled when you see the title, this problem is for you ^_^)
For those who don’t know KTV, see: http://en.wikipedia.org/wiki/Karaoke_box
There is one very popular song called Jin Ge Jin Qu(). It is a mix of 37 songs, and is extremely long (11 minutes and 18 seconds) — I know that there are Jin Ge Jin Qu II and III, and some other unofficial versions. But in this problem please forget about them.
Why is it popular? Suppose you have only 15 seconds left (until your time is up), then you should select another song as soon as possible, because the KTV will not crudely stop a song before it ends (people will get frustrated if it does so!). If you select a 2-minute song, you actually get 105 extra seconds! ....and if you select Jin Ge Jin Qu, you’ll get 663 extra seconds!!! Now that you still have some time, but you’d like to make a plan now. You should stick to the following rules:
• Don’t sing a song more than once (including Jin Ge Jin Qu). • For each song of length t, either sing it for exactly t seconds, or don’t sing it at all. • When a song is finished, always immediately start a new song.
Your goal is simple: sing as many songs as possible, and leave KTV as late as possible (since we have rule 3, this also maximizes the total lengths of all songs we sing) when there are ties.
Input The first line contains the number of test cases T (T ≤ 100). Each test case begins with two positive integers n, t (1 ≤ n ≤ 50, 1 ≤ t ≤ 109), the number of candidate songs (BESIDES Jin Ge Jin Qu) and the time left (in seconds). The next line contains n positive integers, the lengths of each song, in seconds. Each length will be less than 3 minutes — I know that most songs are longer than 3 minutes. But don’t forget that we could manually “cut” the song after we feel satisfied, before the song ends. So here “length” actually means “length of the part that we want to sing”.
It is guaranteed that the sum of lengths of all songs (including Jin Ge Jin Qu) will be strictly larger than t.
Output
For each test case, print the maximum number of songs (including Jin Ge Jin Qu), and the total lengths of songs that you’ll sing.
Explanation: In the first example, the best we can do is to sing the third song (80 seconds), then Jin Ge Jin Qu for another 678 seconds. In the second example, we sing the first two (30+69=99 seconds). Then we still have one second left, so we can sing Jin Ge Jin Qu for extra 678 seconds. However, if we sing the first and third song instead (30+70=100 seconds), the time is already up (since we only have 100 seconds in total), so we can’t sing Jin Ge Jin Qu anymore!
Sample Input
2 3 100 60 70 80 3 100 30 69 70
Sample Output

Case 1: 2 758 Case 2: 3 777

(bb一大摞。。。)

这道题目要求歌曲数目尽量多,时间尽量长,并且根据第一个样例可以看出时间长优先于歌曲数目。

题目原型是01背包问题,可以用滚动数组写,还要求算出花费,花费存在最后一层状态中了,遍历一遍就好。

一般情况下01背包初始化为0就好了,但是这题dp要初始化为负的,我初始化为0答案是错的,还没想明白,初步猜测是这样能让每个状态合法。

代码:

#include 
using namespace std;
typedef long long ll;

int T;
int n, t;
int len[55];
int Case = 0;
int dp[10000], ans;

int main()
{
    scanf("%d", &T);

    while(T--)
    {
        scanf("%d%d", &n, &t);

        for(int i = 1; i <= n; i++)
            scanf("%d", &len[i]);

        memset(dp, -1, sizeof dp);
        dp[0] = 0;
        for(int i = 1; i <= n; i++){
            for(int j = t - 1; j >= len[i]; j--){
                dp[j] = max(dp[j], dp[j - len[i]] + 1);
            }
        }

        ans = 0;
        int pos = -1;
        for(int i = t - 1; i >= 0; i--){
            if(dp[i] > pos)
                pos = dp[i], ans = i;
        }

        printf("Case %d: %d %d\n", ++Case, pos + 1, ans + 678);
    }

    return 0;
}

二。度度熊的午饭时光

度度熊的午饭时光

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 492    Accepted Submission(s): 181


Problem Description
度度熊最期待每天的午饭时光,因为早饭菜品清淡,晚饭减肥不敢吃太多(胖纸的忧伤T.T)。

百度食堂的午餐超级丰富,祖国各大菜系应有尽有,度度熊在每个窗口都有爱吃的菜品,而且他还为喜爱的菜品打了分,吃货的情怀呀(>.<)。

但是,好吃的饭菜总是很贵,每天的午饭预算有限,请帮度度熊算一算,怎样打饭才能买到的最好吃的饭菜?(不超过预算、不重样、午餐等分最高的情况下,选择菜品序号加和最小,加和相等时字典序最小的组合)
 

Input
第一行一个整数T,表示T组数据。
每组测试数据将以如下格式从标准输入读入:

B

N

score_1 cost_1

score_2 cost_2

:

score_N cost_N
  
第一行,正整数B(0 <= B <= 1000),代表午餐的预算。

第二行,正整数N (0 <= N <= 100),代表午餐可选的菜品数量

从第三行到第 (N + 2) 行,每行两个正整数,以空格分隔,score_i表示菜品的得分,cost_i表示菜品的价格(0 <= score_i, cost_i <= 100)。
 

Output
对于每组数据,输出两行:
第一行输出:"Case #i:"。i代表第i组测试数据。
第二行输出菜品的总得分和总花费,以空格分隔。
第三行输出所选菜品的序号,菜品序号从1开始,以空格分隔。
 

Sample Input
 
   
2 29 6 9 10 3 4 6 5 7 20 10 9 15 11 0 2 2 23 10 12
 

Sample Output
 
   
Case #1: 34 29 2 3 5 6 Case #2: 0 0
 

这题也是背包问题,但是多了一个打印路径的要求,而01背包滚动数组的写法丢弃了前n-1层状态,所以如果用一维数组写要记录dp过程中发生转移的位置。

用二维写可以记录过程中的状态,但是怎么确定状态转移那个点?单纯用状态转移方程不能得到正确答案,这时候肯定对规划的边界值和规划方向有要求,

我不怎么想的通,我还是太菜了。。。

代码:

/*
#include 
#include 
#include 
using namespace std;

int b, n;
int w[101], v[101];
int dp[1001];
int cost = 0;
int nextj, num;
bool ans[101], vis[101][1001];

int main()
{
    int t;

    scanf("%d", &t);

    for(int Case = 1; Case <= t; Case++)
    {
        scanf("%d%d", &b, &n);
        for(int i = 1; i <= n; i++){
            scanf("%d%d", &v[i], &w[i]);
        }

        memset(dp, 0, sizeof (dp));
        memset(vis, 0, sizeof vis);
        for(int i = 1; i <= n; i++){
            for(int j = b; j >= w[i]; j--)
            if(dp[j - w[i]] + v[i] > dp[j]){
                dp[j] = dp[j - w[i]] + v[i];
                vis[i][j] = true;
            }else
                vis[i][j] = false;
        }

        memset(ans, 0, sizeof (ans));

        nextj = b;
        cost = num = 0;
        for(int i = n; i >= 0; i--)
        {
            if(vis[i][nextj]){
                ans[i] = true;
                nextj -= w[i];
                cost += w[i];
                num++;
            }
        }

        printf("Case #%d:\n%d %d\n", Case, dp[b], cost);
        for(int i = 0; i <= n; i++){
            if(ans[i])
                printf("%d%c", i, (--num)?' ':'\n');
        }
    }

    return 0;
}
*/
/*
#include 
#include 
#include 
using namespace std;

int b, n;
int w[101], v[101];
int dp[1001];
int cost, num;
bool ans[101];

int main()
{
    int t;

    scanf("%d", &t);

    for(int Case = 1; Case <= t; Case++)
    {
        scanf("%d%d", &b, &n);
        for(int i = 1; i <= n; i++){
            scanf("%d%d", &v[i], &w[i]);
        }

        memset(dp, 0, sizeof (dp));
        //memset(vis, 0, sizeof vis);
        for(int i = 1; i <= n; i++){
            for(int j = b; j >= w[i]; j--)
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        }

        cost = num = 0;
        for(int i = 0; i <= b; i++)
        {
            if(dp[i] > num){
                num = dp[i], cost = i;
            }
        }

        printf("Case #%d:\n%d %d\n", Case, num, cost);
    }

    return 0;
}
*/
#include 
#include 
#include 
using namespace std;

int b, n;
int w[101], v[101];
int dp[101][1001];
int cost = 0;
int nextj, num;
bool ans[101];

int main()
{
    int t;

    scanf("%d", &t);

    for(int Case = 1; Case <= t; Case++)
    {
        scanf("%d%d", &b, &n);
        for(int i = 1; i <= n; i++){
            scanf("%d%d", &v[i], &w[i]);
        }

        memset(dp, 0, sizeof (dp));
        memset(vis, 0, sizeof vis);
        for(int i = 1; i <= n; i++){
            for(int j = b; j >= 0; j--)
                dp[i][j] =
        }

        memset(ans, 0, sizeof (ans));

        nextj = b;
        cost = num = 0;
        for(int i = n; i >= 0; i--)
        {
            if(vis[i][nextj]){
                ans[i] = true;
                nextj -= w[i];
                cost += w[i];
                num++;
            }
        }

        printf("Case #%d:\n%d %d\n", Case, dp[b], cost);
        for(int i = 0; i <= n; i++){
            if(ans[i])
                printf("%d%c", i, (--num)?' ':'\n');
        }
    }

    return 0;
}

注释掉的是我其他写法尝试。。。


三。

Perhaps you have heard of the legend of the Tower of Babylon. Nowadays many details of this tale have been forgotten. So now, in line with the educational nature of this contest, we will tell you the whole story: The babylonians had n types of blocks, and an unlimited supply of blocks of each type. Each type-i block was a rectangular solid with linear dimensions (xi,yi,zi). A block could be reoriented so that any two of its three dimensions determined the dimensions of the base and the other dimension was the height. They wanted to construct the tallest tower possible by stacking blocks. The problem was that, in building a tower, one block could only be placed on top of another block as long as thetwobasedimensionsoftheupperblockwerebothstrictlysmallerthanthecorresponding base dimensions of the lower block. This meant, for example, that blocks oriented to have equal-sized bases couldn’t be stacked. Your job is to write a program that determines the height of the tallest tower the babylonians can build with a given set of blocks. Input The input file will contain one or more test cases. The first line of each test case contains an integer n, representing the number of different blocks in the following data set. The maximum value for n is 30. Each of the next n lines contains three integers representing the values xi, yi and zi. Input is terminated by a value of zero (0) for n. Output For each test case, print one line containing the case number (they are numbered sequentially starting from 1) and the height of the tallest possible tower in the format ‘Case case: maximum height = height’

Sample Input 1 10 20 30 2 6 8 10 5 5 5 7 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 5 31 41 59 26 53 58 97 93 23 84 62 64 33 83 27 0

Sample Output Case 1: maximum height = 40 Case 2: maximum height = 21 Case 3: maximum height = 28 Case 4: maximum height = 342


DAG的动态规划。写过博客,不再写。

四。Lighting System Design

You are given the task to design a lighting system for a huge conference hall. After doing a lot of calculation and sketching, you have figured out the requirements for an energy-efficient design that can properly illuminate the entire hall. According to your design, you need lamps of n different power ratings. For some strange current regulation method, all the lamps need to be fed with the same amount of current. So, each category of lamp has a corresponding voltage rating. Now, you know the number of lamps and cost of every single unit of lamp for each category. But the problem is, you are to buy equivalent voltage sources for all the lamp categories. You can buy a single voltage source for each category (Each source is capable of supplying to infinite number of lamps of its voltage rating.) and complete the design. But the accounts section of your company soon figures out that they might be able to reduce the total system cost by eliminating some of the voltage sources and replacing the lamps of that category with higher rating lamps. Certainly you can never replace a lamp by a lower rating lamp as some portion of the hall might not be illuminated then. You are more concerned about money-saving than energy-saving. Find the minimum possible cost to design the system.
Input Each case in the input begins with n (1 ≤ n ≤ 1000), denoting the number of categories. Each of the following n lines describes a category. A category is described by 4 integers - V (1 ≤ V ≤ 132000), the voltage rating, K (1 ≤ K ≤ 1000), the cost of a voltage source of this rating, C (1 ≤ C ≤ 10), the cost of a lamp of this rating and L (1 ≤ L ≤ 100), the number of lamps required in this category. The input terminates with a test case where n = 0. This case should not be processed.
Output
For each test case, print the minimum possible cost to design the system.
Sample Input
3 100 500 10 20 120 600 8 16 220 400 7 18 0
Sample Output
778

这道题题目就读了好久,这个其实也有最优子结构。刘汝佳定义的状态:

先把灯泡按照电压从小到大排序。 设s[i]为前i种灯泡的总数量(即L值之和),d[i]为灯
1i的最小开销,则d[i] = min{d[j](s[i]s[j])*c[i]k[i])},表示前j个先用最优方案
买,然后第
j1i个都用第i号的电源。 答案为d[n]

这是怎么想出来的真是的。

实现代码时遇到一个小麻烦, 我是在排序前算s的,应该排序后再算。

代码:

#include 
using namespace std;
#define INF 10000000

int n;
struct cat{
    int v, k, c, s;
}a[1002];
int dp[1001];

bool cmp(cat a, cat b)
{
    return a.v < b.v;
}

int main()
{
    while(scanf("%d", &n) != EOF && n)
    {
        memset(a, 0, sizeof a);
        for(int i = 1; i <= n; i++){
            scanf("%d%d%d%d", &a[i].v, &a[i].k, &a[i].c, &a[i].s);
        }

        sort(a + 1, a + n + 1, cmp);
        for(int i = 2; i <= n; i++){
            a[i].s += a[i - 1].s;
        }
        a[0].s = a[0].v = a[0].c = a[0].k = 0;

        fill(dp, dp + n + 1, INF);
        dp[0] = 0;
        for(int i = 1; i <= n; i++){
            for(int j = i; j >= 0; j--)
                dp[i] = min(dp[i], dp[j] + (a[i].s - a[j].s) * a[i].c + a[i].k);
        }

        printf("%d\n", dp[n]);
    }

    return 0;
}

这两天写这些简单的DP题就要死要活的,而且还没有真的理解透。比如打印问题我还是懵逼。


五。Partitioning by Palindromes

We say a sequence of characters is a palindrome if it is the same written forwards and backwards. For example, ‘racecar’ is a palindrome, but ‘fastcar’ is not. A partition of a sequence of characters is a list of one or more disjoint non-empty groups of consecutive characters whose concatenation yields the initial sequence. For example, (‘race’, ‘car’) is a partition of ‘racecar’ into two groups. Given a sequence of characters, we can always create a partition of these characters such that each group in the partition is a palindrome! Given this observation it is natural to ask: what is the minimum number of groups needed for a given string such that every group is a palindrome?
For example: • ‘racecar’ is already a palindrome, therefore it can be partitioned into one group. • ‘fastcar’ does not contain any non-trivial palindromes, so it must be partitioned as (‘f’, ‘a’, ‘s’, ‘t’, ‘c’, ‘a’, ‘r’). • ‘aaadbccb’ can be partitioned as (‘aaa’, ‘d’, ‘bccb’).
Input
Input begins with the number n of test cases. Each test case consists of a single line of between 1 and 1000 lowercase letters, with no whitespace within.
Output
For each test case, output a line containing the minimum number of groups required to partition the input into groups of palindromes.
Sample Input
3 racecar fastcar aaadbccb
Sample Output
1 7 3

首先先预处理任意i, j之间是否回文。

然后设计状态d(i)表示1~i之间的最小划分。

状态转移方程d(i) = d(j) + 1 (如果j+1~i回文)。

#include 
using namespace std;
#define INF 100000000

int main()
{
    int n;

    scanf("%d", &n);

    while(n--)
    {
        char s[1001];
        bool palin[1001][1001];
        int dp[1001];
        int ls;

        s[0] = ' ';
        scanf("%s", s + 1);
        ls = strlen(s);
        ls--;

        memset(palin, false, sizeof(palin));
        for(int i = 1; i <= ls; i++){
            palin[i][i] = true;
            for(int j = 0; j < i; j++){
                palin[i][j] = true;
                for(int k = j, l = i; l > k; k++, l--){
                    if(s[k] != s[l]){
                        palin[i][j] = false;
                        break;
                    }
                }
            }
        }

        fill(dp, dp + ls + 1, INF);
        dp[0] = 0;
        for(int i = 1; i <= ls; i++){
            dp[i] = i;
            for(int j = 0; j < i; j++){
                if(palin[i][j + 1]){
                    dp[i] = min(dp[i], dp[j] + 1);
                }
            }
        }

        printf("%d\n", dp[ls]);
    }

    return 0;
}



你可能感兴趣的:(算法竞赛入门经典紫书)