【问题描述】
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <string.h>
#define MAXN 200
int main()
{
int n, a[MAXN], b[MAXN], c[MAXN], i, j, max;
scanf("%d", &n);
for (i = 1; i <= n; i++)
scanf("%d", &a[i]);
memset(b, 0, sizeof(a));
memset(c, 0, sizeof(c));
b[1] = 1;
for (i = 2; i <= n; i++)
{
max = 0;
for (j = i - 1; j >= 1; j--)
{
if (a[j] < a[i] && b[j] + 1 > max)
max = b[j];
}
b[i] = max + 1;
}
c[n] = 1;
for (i = n - 1; i > 0; i--)
{
max = 0;
for (j = i + 1; j <= n; j++)
{
if (a[j] < a[i] && c[j] + 1 > max)
max = c[j];
}
c[i] = max + 1;
}
max = b[1] + c[1];
for (i = 2; i <= n; i++)
{
if (b[i] + c[i] > max)
max = b[i] + c[i];
}
printf("%d\n", n - max + 1);
return 0;
}
标签归档:dynamic programming
滑雪 pku1088 动态规划
【Description】
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
【Input】
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
【Output】
输出最长区域的长度。
【Sample Input】
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
【Sample Output】
25
【Source】
SHTSC 2002
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | package pku1088; /**深度搜索+备忘录方法 / 动态规划 参考了pku牛人的解题报告*/ import java.util.*; public class Main { /** * @param args */ private static int[][] a = new int[101][101]; private static int[][] b = new int[101][101];// 备忘录 private static boolean[][] flag = new boolean[101][101]; private static int r, c; private static int[] tempx = { 1, 0, -1, 0 }; private static int[] tempy = { 0, -1, 0, 1 }; public static int trydo(int x, int y) { if (flag[x][y])// 如果已经有了,直接输出,备忘录方法快就快在这里 return b[x][y]; int max = 1, temp; for (int i = 0; i < 4; i++) {// 四个方向 int xx = x + tempx[i]; int yy = y + tempy[i]; if (xx >= 0 && xx < r && yy >= 0 && yy < c) {// 判断是否出界 if (a[xx][yy] < a[x][y]) {// 如果要到的点到本点低 temp = trydo(xx, yy);// 走下一个点 if (temp + 1 > max)// max = temp + 1; } } } flag[x][y] = true;// 已经走过 b[x][y] = max;// 存入备忘录 return max; } public static void main(String[] args) { // TODO Auto-generated method stub Scanner cin = new Scanner(System.in); r = cin.nextInt(); c = cin.nextInt(); for (int i = 0; i < r; i++) { for (int j = 0; j < c; j++) { a[i][j] = cin.nextInt(); b[i][j] = 0; flag[i][j] = false; } } int max = 0; for (int i = 0; i < r; i++) {// 生成每个点的最大 for (int j = 0; j < c; j++) { if (trydo(i, j) > max) max = trydo(i, j); } } System.out.println(max); } } |
青蛙过河
【描述】
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
对于30%的数据,L <= 10000;
对于全部的数据,L <= 10^9。
【输入格式 Input Format】
输入的第一行有一个正整数L(1 <= L <= 10^9),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1 <= S <= T <= 10,1 <= M <= 100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
【输出格式 Output Format】
输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。
【算法描述】
转某牛人的解题报告!!!!
这道题在没看数据规模之前以为是一道简单的DP,但是数据开到十亿,无论在时间还是空间复杂度都过大,所以就要进行优化了。
解一:
简单方法:预期得分30。简单动态规划,f[i]代表青蛙跳到i点时所可能踩到的最少石子数,所以有f[i]=min{f[k]+map[i]}(i-s≤k≤i-t),其中map[i]代表i上是否有石子,有是1,否则0。算法复杂度O(n^2)。
解二:
改进方法:预期得分100。我们会发现,虽然桥很长,但上面最多只有100个石子,想到能否用石子DP,而应该是不行的。那能否基于第一种方法?由于石子排布非常的疏,我们还会发现,如果两个石子相隔甚远,那他们中间的f[i]大部分将会是同一个数,能否把两个石子的距离缩短,使之还与原来等效?要是行的话怎么缩?王乃岩同学考试时做了一个方法能够过全部数据,用的滚动数组存储,下面列出了他的程序。我自己也写了个程序,和他不尽相同:我令L=stone[i]-stone[i-1](stone[i]代表按坐标由小到大顺序排列的石块坐标),当L能够被t整除时(L%t==0),令k=t;当L不能被t整除时(L%t!=0),令k=L%t。然后令k为k+t,最后判断如果k>L,那么map[]数组中stone[i]和stone[i-1]两石头的距离就被等效成为L(也就是没变);如果k<=L,那么map[]数组中stone[i]和stone[i-1]两石头的距离就被等效成为k,可以看出来,这样处理完,两石子最大间距为2*t,大大的缩短了数组,再按解一进行DP,就可以通过了。
【代码】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | #include <stdio.h> #include <string.h> long stone[101]; int map[100001]; int f[100001]; long L; int S, T, M; void quickSort(int l, int r) { int i , j; long temp; i = l; j = r; temp = stone[i]; while (i < j) { while (i < j && stone[j] > temp) j--; if (i < j) { stone[i] = stone[j]; i++; } while (i < j && stone[i] < temp) i++; if (i < j) { stone[j] = stone[i]; j--; } } stone[i] = temp; if (i - 1 > l) quickSort(l, i - 1); if (i + 1 < r) quickSort(i + 1, r); } int main() { int i, j; long l, k, p = 0, min; scanf("%ld%d%d%d", &L, &S, &T, &M); for (i = 1; i <= M; i++) scanf("%ld", &stone[i]); memset(map, 0, sizeof(int)*100001); memset(f, 0, sizeof(int)*100001); quickSort(1, M); stone[0] = 0; p = 0; for (i = 1; i <= M; i++) { l = stone[i] - stone[i - 1]; if (l % T == 0) k = T; else k = l % T; k = k + T; if (l < k) k = l; p = p + k; map[p] = 1; } for (i = 1; i <= p + T; i++) { min = 1000; for (j = i - T; j <= i - S; j++) if ( j >= 0 && f[j] < min) min = f[j]; f[i] = min + map[i]; } min = 1000; for (i = p + 1; i <= p + T; i++) if (f[i] < min) min = f[i]; printf("%d\n", min); return 0; } |