两道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;
}
用二维写可以记录过程中的状态,但是怎么确定状态转移那个点?单纯用状态转移方程不能得到正确答案,这时候肯定对规划的边界值和规划方向有要求,
我不怎么想的通,我还是太菜了。。。
代码:
/*
#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]为灯
泡1~i的最小开销,则d[i] = min{d[j]+ (s[i]-s[j])*c[i]+ k[i])},表示前j个先用最优方案
买,然后第j+1~i个都用第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;
}
五。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;
}