北航OJ-2014级第2次算法上机题解
A.零崎的补番计划Ⅰ
题目描述
零崎是个很喜欢逛b站的人,除了Korea相关和哲♂学,零崎什么都看。b站每天都有好多up主更新视频,零崎自然不可能每个视频都看,而且零崎平时在学校又忙着各种各样的社(da)团(ma)活(jiang)动,所以零崎一贯在放假的时候补番。
一般来说,零崎会按照b站的评分挑选出最大的k个来补,不过零崎倒也不在意最终补番的顺序,所以你们只要把第k大的评分找出来,之后零崎自己去选出所有比第k个大的就可以了。
输入
多组测试数据。 对于每组数据,第一行为两个整数n与k,表示有n个视频,零崎要补k部番(1<=k<=n<=1000000)。假定评分均不相同。
第二行包含n个整数,用空格隔开,为各种各样的评分。## 输出 对于每组数据,输出一个整数,为排名k的视频的分数。
输入样例
5 3
1 3 4 2 5
5 1
5 7 6 8 2
输出样例
3
8
Hint
这是一个排序,这又不是一个排序。
解题思路:
这题考查的算法是第i顺序量查找。
但是和CLRS上的算法有所不同的是,这题要求查找的是从大到小的第i个量。解决方法有两种:
(1) 改写partion函数,使其变成能够直接返回从大到小的第i个量的方法;
(2)换种思路,对于一个长度为n的数组,第i个值最大的值,就是最小的第n-i个值。(很明显这种方法更简单啦~~)。
直观感受一下 (A[n]是一个升序排列好的数组):
解题代码:
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
//全部都是逻辑位置 不是物理位置
//把A[q]归位,返回它现在所在的位置
int partion(int A[], int low, int high)//选择最右边的A[q]作为主元;
{
int x = A[high];
int i = low-1;
for(int j = low; j<high; j++)
{
if(A[j]<=x)//加上=号很重要,为了把相等于主元(即A[q])的元素全部都集中到中部
swap(A[++i], A[j]);
}
swap(A[++i], A[high]);
return i;
}
//把A[i]归位
int randomPartion(int A[], int low, int high)
{
int i = rand()%(high-low+1)+low;
swap(A[i], A[high]);
return partion(A,low,high);
}
//判断p<q是否成立
int randomSelect(int A[], int low, int high, int i)
{
if(low==high)
return A[low];
int temp = randomPartion(A, low, high);
int order = temp-low+1;
if(i==order)
return A[temp];
else if(i<order)
return randomSelect(A, low, temp-1, i);
else
return randomSelect(A, temp+1, high, i-order);
}
int A[1000001];
//对于所有的逻辑位置,输入的所有i的位置,我们默认为其中的物理位置
int main()
{
srand(time(NULL));
int n, k;
while(scanf("%d%d", &n,&k)!=EOF)
{
getchar();
for(int i = 0; i<n; i++)
scanf("%d",&A[i]);
if(k>0 && k<=n)
printf("%d\n", randomSelect(A, 0, n-1, n-k+1));
}
}
B.零崎的补番计划Ⅱ
题目描述
虽然零崎已经有了补番目录,然而零崎发现就算是假期,他也有各(da)种(ma)各(jiang)样的事情要做,所以零崎现在要在有限的时间内尽量补完更有价值看的视频。
零崎的假期一共有T时间,现在有k个视频,每个视频的价值为v,时间长度为t,零崎会好好看完不会随意快进。
输入
多组测试数据。
每组数据第一行为两个整数T和k,表示总时间和视频数量。
接下来k行,每行两个数据vi,ti代表第i个视频的价值和时长。
1<=T<=200000,1<=k<=300,1<=v,t<=200
输出
对于每组数据,输出一行,为零崎能看完的视频的价值总和的最大值。
输入样例
6 3
1 2
2 3
3 2
2 4
3 1
2 1
1 3
1 5
输出样例
5
5
解题思路:
这道题目考察0-1背包问题。
0-1 背包问题是一个DP问题,但是当时在学习它的时候,并没有像学习钢管切割问题一样很快就理解了。原因在于:钢管切割问题是一维的,也就是说动态变化的量仅仅是钢管的长度,只需要对于钢管长度进行DP求解就可以了;但是0-1背包问题则需要同时对于装入背包的物品个数以及背包容量这两个量进行二维的DP求解。
但是还是遵循DP的本质思路:利用状态转移方程,用保存下来的子问题的解构建规模更大的父问题的解,即“由小构大”。
我用我自己的语言叙述一下0-1背包问题的解决方法:
0-1背包问题: 已知背包最大容量W,以及N个物品的重量weight[]和价值value[],求如何装,才能使得背包所装物品的总价值最大。
先引入一个记号:maxVal[i][w]。maxVal[i][w]表示: