目录
题目描述找出数组中第 k 小的数
二分查找
题目描述
给定一个元素升序的整型数组 T 和一个目标值 x ,请使用二分查找法结合分治策略查找 T 中的 x,如果目标值存在输出下标,否则输出 -1。
输入的第一行为数组 T 的各个元素,输入的第二行为目标值 x。
样例输入输出
样例1
输入:
-1 0 1 3 6 10
6输出:
4
样例2
输入:
-1 0 1 3 6 10
2输出:
-1
#include<iostream>
using namespace std;
int binarySearch(int list[], int n, int x)
{
int left = 0, right = n;//记录序列左端和右端的下标
int mid = (left + right) / 2;//记录中点下标
while (left <= right)//每次二分都会使序列缩小,当序列缩小为左端大于右端时,即说明找遍了整个序列仍未找到
{
if (list[mid] == x) {//判断中点值与x是否相等,相等则返回下标
return mid;
}
else if (x < list[mid])//如果中点值大于x,则查找序列变为mid左边的序列,即让右端点变为mid-1
{
right = mid - 1;
}
else//如果中点值小于x,则查找序列变为mid右边的序列,即让左端点变为mid+1
{
left = mid + 1;
}
mid = (left + right) / 2;
}
return -1;
}
int main()
{
int i = 0;
int *a=new int[30];
int num;
while (cin >> num){
a[i++] = num;
if (cin.get() == '\n')
break;
}
int k;
cin >> k;
cout<< binarySearch(a, i, k);
return 0;
}
找出数组中第 k 小的数
题目描述
给定整型数组 S 和整数 k,S的长度为n,1<= k <= n,请使用分治算法输出数组中第 k 小的数。
输入的第一行为数组 S 的各个元素,输入的第二行为整数 k。
样例输入输出
样例1
输入:
3 1 2 5 4 6
3输出:
3
样例2
输入:
2 2 5 1 1 4 3 6 3
2输出:
1
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
void merge(int a[], int left, int mid, int right) //二分归并排序
{
int i, k;
int* tmp = (int*)malloc((right - left + 1) * sizeof(int));
int left1 = left;
int left2 = mid;
int right1 = mid + 1;
int right2 = right;
for (k = 0; left1 <= left2 && right1 <= right2; k++)
{
if (a[left1] <= a[right1])
{
tmp[k] = a[left1++];
}
else
{
tmp[k] = a[right1++];
}
}
if (left1 <= left2)
{
for (i = left1; i <= left2; i++)
{
tmp[k++] = a[i];
}
}
if (right1 <= right2)
{
for (i = right1; i <= right2; i++)
{
tmp[k++] = a[i];
}
}
for (i = 0; i < right - left + 1; i++)
{
a[left + i] = tmp[i];
}
free(tmp);
return;
}
void merge_sort(int a[], int left, int right)
{
int mid = 0;
if (left < right)
{
mid = (left + right) / 2;
merge_sort(a, left, mid);
merge_sort(a, mid + 1, right);
merge(a, left, mid, right);
}
return;
}
int select(int a[], int left, int right, int k)
{
int n = right - left;
if (n < 5)
{
merge_sort(a, left, right - 1);
return a[left + k - 1];
}
int i;
int s = n / 5;
int* m = new int[s];//中位数数组
for (i = 0; i < s; i++)
{
merge_sort(a, left + i * 5, left + i * 5 + 5 - 1);
m[i] = a[left + i * 5 + 2];
}
merge_sort(m, 0, i - 1);
int mid = m[i / 2];
int* a1 = new int[n];
int* a2 = new int[n];
int* a3 = new int[n];
int num1 = 0, num2 = 0, num3 = 0;
for (int i = left; i < right; i++)
{
if (a[i] < mid)
{
a1[num1++] = a[i];
}
else if (a[i] == mid)
{
a2[num2++] = a[i];
}
else
a3[num3++] = a[i];
}
if (num1 >= k)
{
return select(a1, 0, num1, k);
}
if (num1 + num2 >= k)
{
return mid;
}
else
return select(a3, 0, num3, k - num1 - num2);
}
int main()
{
int i = 0;
int *a=new int[30];
int num;
while (cin >> num){
a[i++] = num;
if (cin.get() == '\n')
break;
}
int k;
cin >> k;
cout<< select(a, 0, i, k);
return 0;
}
平面最邻近点对
题目描述
给定平面上 n 个点,使用分治算法输出其中的一对点的距离,使得在这 n 个点的所有点对中,该距离为所有点对中最小的,输出结果四舍五入保留 4 位小数。
输入的第一行为整数 n ,表示点的个数。
接下来输入的 n 行,每行两个整数 x, y ,表示一个点的横坐标和纵坐标。
样例输入输出
样例1
输入:
3
1 1
1 2
2 2输出:
1.0000
样例2
输入:
5
1 2
3 5
4 7
2 6
9 9输出:
1.4142
#include <iostream>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;
//结构体储存点的坐标
struct node {
int x, y;
}p[50];
//求两点间的距离
double dis(node& a, node& b) {
return sqrt(pow((a.x - b.x), 2) + pow((a.y - b.y), 2));
}
//比较
int cmpx(node& a, node& b) {
return a.x < b.x;
}
//求最近点对
double md(node p[], int n) {
double mind = 0x3f3f3f3f;
if (n == 1)return 0;
else if (n <= 3) {//蛮力算法
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i != j)
mind = min(mind, dis(p[i], p[j]));
}
}
}
else {//分治
node l[50], r[50], s[50];
double m, dl, dr, d;
int len1 = ceil(n / 2), len2 = n - len1, num = 0;
for (int i = 0; i < len1; i++)
l[i] = p[i];
for (int i = 0; i < len2; i++)
r[i] = p[i + len1];
//递归,求左右两段各自的最近点对
dl = md(l, len1);
dr = md(r, len2);
d = min(dl, dr);
mind = d;
m = p[len1 - 1].x;//分割处x的坐标值
for (int i = 0; i < n; i++) {
if (fabs(p[i].x - m) < d)
s[num++] = p[i];
}
//比较左右最近点对距离与跨越左右点对距离
for (int i = 0; i < num - 1; i++) {
int k = i + 1;
while (k < num) {
mind = min(mind, dis(s[i],s[k]));
k++;
}
}
}
return mind;
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >>p[i]. x >>p[i].y ;
}
sort(p, p + n, cmpx);//按x升序排列
cout << fixed << setprecision(4) << md(p,n) << endl;
return 0;
}
最长公共子序列
题目描述
给出字符串str1和str2,如果一个字符串s同是str1和str2的子序列,则称s为二者的公共子序列,如果s最长,则称s为最长公共子序列,即LCS.
尝试使用动态规划的方法找出给定字符串的最长公共子序列长度.
输入的第一行为字符串str1.
输入的第二行为字符串str2.
样例输入输出
样例1
输入:
abcde
ace输出:
3
样例2
输入:
qwe
dfg输出:
0
import java.util.Scanner;
public class LCS {
public int find(String a,String b) {
int n=a.length();
int m=b.length();
char[]a1=a.toCharArray();
char[]b1=b.toCharArray();
int [][]c=new int[n][m];
for(int i=0;i<n;i++) {
if(a1[i]==b1[0]) {
c[i][0]=1;
for(int j=i+1;j<n;j++) {
c[j][0]=1;
}
break;
}
}
for(int i=0;i<m;i++) {
if(a1[0]==b1[i]) {
c[0][i]=1;
for(int j=i+1;j<m;j++) {
c[0][j]=1;
}
break;
}
}
for(int i=1;i<n;i++) {
for(int j=1;j<m;j++) {
if(a1[i]==b1[j]) {
c[i][j]=c[i-1][j-1]+1;
}
else c[i][j]=Math.max(c[i-1][j],c[i][j-1]);
}
}
return c[n-1][m-1];
}
public static void main(String[] args) {
// TODO Auto-generated method stub
LCS lcs=new LCS();
Scanner in=new Scanner(System.in);
String s1=in.nextLine();
String s2=in.nextLine();
System.out.println(lcs.find(s1, s2));
in.close();
}
}
背包问题
题目描述
现在有一个容量为v的背包和n件体积不同价值不同的物品,应该如何选择装入物品使背包中的物品总价值最大.
要求使用动态规划的思维计算出背包能装下的最大物品总价值.
输入第一行为两个整数,n和v,n表示总的物品数量,v表示背包的容量.
接下来的n行每行包括两个整数,分别表示物品的体积和价值.
提示:
对于40%的数据,n≤10;
对于100%的数据,n≤50.
样例输入输出
样例1
输入:
5 20
5 3
7 4
8 2
6 1
3 8输出:
15
样例2
输入:
5 50
25 15
16 9
2 3
8 8
6 4输出:
32
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 50;
int w[N], v[N], d[N];
int main() {
memset(d, 0, sizeof(d));
memset(w, 0, sizeof(w));
memset(v, 0, sizeof(v));
int maxv, n;
cin >> n >> maxv;
for (int i = 1; i <= n; ++i) {
cin >> w[i] >> v[i];
for (int j = maxv; j >= w[i]; --j) {
d[j] = max(d[j], d[j - w[i]] + v[i]);
}
}
cout << d[maxv] << endl;
return 0;
}
矩阵连乘
题目描述
设A1, A2, ... , An为矩阵序列,给定长度为n+1的列表P表示矩阵的规模,例如:Ai 为 【Pi-1×Pi】阶矩阵,i = 1, 2, ... , n.
Ai与Ai+1矩阵相乘的时,元素相乘次数为Pi-1×Pi×Pi+1.
使用动态规划求解矩阵连乘问题,输出最少乘法次数.
输入的第一行为1个整数n,表示矩阵数量.
输入的第二行为n+1个整数,表示n个矩阵的行数、列数.
提示:
对于40%的样例,n≤10
对于100%的样例,n≤50
样例输入输出
样例2
输入:
6
30 35 15 5 10 20 25输出:
15125
样例1
输入:
3
10 100 5 50输出:
7500
#include<iostream>
using namespace std;
#define N 50
int p[N];
int m[N][N];
int main()
{
int n,i,j,k,min,cut;
while(cin>>n)
{
for(i=1;i<=n+1;i++)
cin>>p[i];
for(i=1;i<=n;i++)
m[i][i]=0;//填充正对角线
for(j=2;j<=n;j++)//表示除正对角线外,需要做多少次对角线填充
{
for(i=1,k=j;i<n,k<=n;i++,k++)//遍历对角线元素
{
min=m[i][i]+m[i+1][k]+p[i]*p[i+1]*p[k+1];
for(cut=i+1;cut<k;cut++)//分割,取最小值
{
if(m[i][cut]+m[cut+1][k]+p[i]*p[cut+1]*p[k+1]<min)
min=m[i][cut]+m[cut+1][k]+p[i]*p[cut+1]*p[k+1];
}
m[i][k]=min;
}
}
cout<<m[1][n]<<endl;
}
return 0;
}
最优二分检索树
题目描述
二分检索树为一个二叉树结构,一个节点的左子树的所有节点的值都小于该节点的值,一个节点的右子树的所有节点值都大于该节点的值.
对一个给定的二分检索树,查找一个元素的比较次数定义为:当该元素存在于二分检索树中,比较次数为相应节点(深度+1);当该元素不存在于二分检索树中,比较次数为对应叶节点(深度+1).
最优二分检索树即为平均比较次数最小的二分检索树.
对于一个数据集 S=[x1, x2, ... , xn]包含n个元素 ( 默认x0=-∞,xn+1=+∞ ) ,给定相应的存取概率分布P=[a0, b1, a1, b2, ..., bn, an]包含2n+1个元素.
其中x在xi的概率为bi,x在(xi, xi+1)的概率为ai.
使用过动态规划的方法,构造最有二分检索树,并给出最小的平均比较次数(保留两位小数).
输入
第一行为一个整数n,表示元素数量.
第二行为2n+1个浮点数,表示相应元素的存取概率.
提示:
对于40%的数据,n<10;
对于100%的数据,n<30.
样例输入输出
样例1
输入:
5
0.04 0.1 0.02 0.3 0.02 0.1 0.05 0.2 0.06 0.1 0.01输出:
2.04
样例2
输入:
3
0.1 0.35 0.2 0.05 0.1 0.15 0.05输出:
1.85
#include<iostream>
using namespace std;
float besttimes(int n, float* p) {
float** w = new float* [n + 1];
for (int i = 0; i < n + 1; i++)w[i] = new float[n + 1];
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < n + 1; j++) {
w[i][j] = 0;
for (int k = i; k <= j; k++) {
w[i][j] = w[i][j] + p[k * 2 - 1] + p[k * 2];
}
w[i][j] = w[i][j] + p[j * 2 + 1];
}
}
float** m = new float* [n + 2];
for (int i = 0; i < n + 2; i++)m[i] = new float[n + 2];
for (int i = 0; i < n + 2; i++) {
for (int j = 0; j < n + 2; j++)m[i][j] = 0;
}
for (int i = n; i > 0; i--) {
for (int j = 1; j < n + 1; j++) {
if (i > j)continue;
float min = 99999999;
for (int k = i; k <= j; k++) {
if (m[i][k - 1] + m[k + 1][j] < min) {
min = m[i][k - 1] + m[k + 1][j];
}
}
m[i][j] = min + w[i][j];
}
}
return m[1][n];
}
int main() {
int n=30;
cin >> n;
float *m = new float[2 * n + 2];
float a[15];
float b[15];
for (int i = 1; i < 2 * n + 2; i++) {
cin >> m[i];
}
cout << besttimes(n, m);
return 0;
}
最小延迟调度
题目描述
给定等待服务的客户集合 A = {1, 2, 3, ..., n},预计对客户 i 的服务时间是 ti,该客户希望的完成时间是 di,即 T = {t1, t2, t3, ..., tn},D = {d1, d2, d3, ..., dn}。如果对客户 i 的服务在 di 之前结束,那么对客户 i 的服务没有延迟;如果在 di 之后结束,那么这个服务就被延迟,延迟时间等于该服务结束的时间减去 di。一个调度是函数 f : A -> N,f(i) 为客户 i 的服务开始时间,一个调度 f 的最大延迟是所有客户延迟时间的最大值,请使用贪心算法求出使最大延迟达到最小的调度,输出该调度的最大延迟时间。
输入的第一行是整数 n,表示一共 n 个客户。
接下来输入的 n 行,每行2个整数,分别表示该客户的服务时间和希望完成时间。
样例输入输出
样例1
输入:
5
5 10
8 12
4 15
10 11
3 20输出:
12
#include<iostream>
#include<algorithm>
using namespace std;
struct client {
int t;//任务时长
int d;//期望完成的时间
}c[100];
bool comp(client& a, client& b) {
return a.d < b.d;
}
int main() {
int n=50;
cin >> n;
int start = 0;
int *delay=new int[n];
for (int i = 0; i < n; i++) {
cin >> c[i].t >> c[i].d;
}
sort(c,c+n, comp);//按照di从小到大排列
for (int i = 0; i < n; i++) {
if (c[i].d > start + c[i].t)
delay[i] = 0;
else {
delay[i] = start + c[i].t - c[i].d;
}
start += c[i].t;
}
int max = 0;
for (int i = 0; i < n; i++) {
if (delay[i] > max)
max = delay[i];
}
cout << max << endl;
return 0;
}
活动选择问题
题目描述
刚上大学的小明对各种社团活动都感觉到新奇,本学期学校社团开展了许多活动,但他在同一时间只能参加一个活动。如果一项活动的结束时间恰好等于另一项活动的开始时间,则小明可以参加这两个活动。小明想尽可能多地参加活动,请使用贪心算法帮助他计算出他最多可以参加多少个活动。
输入的第一行是整数 n,表示一共开展了 n 个活动。
接下来输入的 n 行,每行2个整数,分别表示该活动的开始时间和结束时间。
样例输入输出
样例1
输入:
10
1 4
3 5
2 6
5 7
4 9
5 9
6 10
8 11
8 12
2 13输出:
3
#include<iostream>
using namespace std;
void sort(int n, int s[], int f[]) {
//将活动按结束时间从小到大排序
int a, b, i, j;
for (i = 0; i < n; i++) {
for (j = i + 1; j < n; j++) {
if (f[i] > f[j]) {
a = f[i]; f[i] = f[j]; f[j] = a;
b = s[i]; s[i] = s[j]; s[j] = b;
}
}
}
}
int greedyselect(int n, int s[], int f[]) {
int j = 0, sum = 1;//sum为安排进的活动总数
for (int i = 1; i < n; i++) {//先安排第一个活动
if (s[i] >= f[j]) {
j = i;
sum++;
}
}
return sum;
}
int main() {
int n=50;
cin >> n;
int* s = new int[n];
int* f = new int[n];
for (int i = 0; i < n; i++) {
cin >> s[i] >> f[i];
}
sort(n, s, f);
cout<<greedyselect(n, s, f);
return 0;
}
最短路径
题目描述
某地共有 n 个村庄,各个村庄编号即0, 1, 2, ..., n-1,邮递员从0号村庄出发分别给其它各村庄送信,请使用贪心算法计算出邮递员到其它各村庄的最短距离之和。
输入的第一行是整数 n,表示一共 n 个村庄。
输入的第二行是整数 m,表示一共有 m 条路。
接下来输入的 m 行,每行3个整数,分别表示村庄 x,村庄 y,以及 x 到 y的距离。
样例输入输出
样例1
输入:
6
10
0 1 10
0 5 3
1 2 7
1 3 5
3 2 4
3 4 7
3 0 3
5 1 2
5 3 6
5 4 1输出:
33
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 99999;
struct edge
{
int bg, ed, val;
}e[1000];
int dijkstra(int nsum, int esum, edge* e, int center) {
int** t = new int* [nsum];//邻接矩阵
for (int i = 0; i < nsum; i++)t[i] = new int[nsum];
for (int i = 0; i < nsum; i++) {
for (int j = 0; j < nsum; j++) {
if (i == j)t[i][j] = 0;
else t[i][j] = inf;
}
}
for (int i = 0; i < esum; i++)t[e[i].bg][e[i].ed] = e[i].val;
bool* exist = new bool[nsum];//记录结点是否被加入
int* v = new int[nsum];//储存各节点到源点的距离
for (int i = 0; i < nsum; i++) {
exist[i] = 0;
v[i] = t[center][i];
}
for (int i = 0; i < nsum - 1; i++) {
int min = inf;
int node = 0;
for (int j = 0; j < nsum; j++) {
if (v[j] < min && exist[j] == 0) {
min = v[j];
node = j;
}
}
exist[node] = 1;
for (int j = 0; j < nsum; j++) {
if (v[node] + t[node][j] < v[j]) {
v[j] = v[node] + t[node][j];
}
}
}
int sum = 0;
for (int i = 0; i < nsum; i++)sum += v[i];
return sum;
}
int main() {
int n;
int m;
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> e[i].bg >> e[i].ed >> e[i].val;
}
cout << dijkstra(n, m, e, 0);
return 0;
}
安全培训
题目描述
大学有很多学院,现在学校为了科普普及安全知识,需要对每个学院的一位安全委员进行培训,但每个安全委员的空闲时间不同,学校想根据各个学院安全委员的空闲时间安排培训会,请使用贪心算法计算出学校需要安排最少几次培训会。
注意:不需要考虑培训会持续时间。即假设如果 X 同学空闲时间段为1 2,Y 同学空闲时间段为2 3,培训会安排在2这个时刻时X 、Y 同学均可参加。
输入的第一行是整数 n,表示该学校共有 n 个学院。
接下来输入的 n 行,每行2个整数,分别表示该安全委员空闲时间段的开始时刻和结束时刻。
样例输入输出
样例1
输入:
4
1 2
2 3
3 4
4 5输出:
2
#include<iostream>
using namespace std;
struct times
{
int bg, ed;
}t[10000];
struct times2 {
int bg, ed;
}t2[10000];
int leasttimes(int n, times* t) {
//按结束时间从小到大排
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (t[i].ed > t[j].ed) {
int a = t[i].bg; t[i].bg = t[j].bg; t[j].bg = a;
int b = t[i].ed; t[i].ed = t[j].ed; t[j].ed = b;
}
}
}
int sum = 1;//记录会议次数
t2[0].bg = t[0].bg;
t2[0].ed = t[0].ed;
for (int i = 1; i < n; i++) {
if (t[i].bg > t2[i - 1].ed) {
sum++;
t2[i].bg = t[i].bg;
t2[i].ed = t[i].ed;
}
else {
t2[i].bg = t2[i - 1].bg;
t2[i].ed = t2[i - 1].ed;
}
}
return sum;
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> t[i].bg >> t[i].ed;
}
cout << leasttimes(n, t);
return 0;
}
N皇后问题
题目描述
将 n 个皇后放置在 n × n 的棋盘上,应该如何放置皇后的位置,保证皇后彼此之间不能相互攻击。
使用回溯算法计算n 皇后问题不同的解决方案的数量。输入第一行为一个整数n,表示皇后的数量和棋盘的规模。
样例输入输出
样例1
输入:
1
输出:
1
样例2
输入:
4
输出:
2
#include <iostream>
#include <cmath>
#include <cstdlib>
using namespace std;
class Queen {
private:
int n;
int* x;
int sum;
public:
Queen(int n) {
this->n = n;
sum = 0;
x = new int[n + 1];
for (int i = 0; i <= n; i++)
x[i] = 0;
}
int getSum() {
return sum;
}
void Backtrack(int t) {
if (t > n) {
sum++;
}
else
for (int i = 1; i <= n; i++) {
x[t] = i;
if (constraint(t))
Backtrack(t + 1);
}
}
bool constraint(int k) {// 约束条件
for (int i = 1; i < k; i++)
if ((abs(k - i) == abs(x[i] - x[k])) || (x[i] == x[k]))
return false;
return true;
}
~Queen() {
delete[] x;
}
};
int main() {
int n;
cin >> n;
Queen queen(n);
queen.Backtrack(1);
cout<<queen.getSum() << endl;
return 0;
}
括号生成问题
题目描述
有效的括号组合表示为,对于每一个“(”在它的右侧都已一个对应的“)”,同时对于每一个“)”在它的左侧都有一个对应的“(”。
现给定括号的对数n,请使用回溯算法的思路求出所有有效的括号组合数量。
输入第一行为一个整数n,表示括号的对数。
样例输入输出
样例1
输入:
3
输出:
5
样例2
输入:
1
输出:
1
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Solution {
void dfs(vector<string>& ans, string cur, int left, int right, int n) {
if (left == n && right == n) {//在左边和右边剩余的括号数都等于 n的时候结算。
ans.push_back(cur);
return;
}
if (left < right) {//右边剩余可以使用的括号数量一定得在严格小于左边剩余的数量的时候,才可以产生分支
return;//直接返回,剪支
}
if (left < n) {
dfs(ans, cur + '(', left + 1, right, n);//左括号都有小于n个,产生分支
}
if (right < n) {
dfs(ans, cur + ')', left, right + 1, n);//右括号都有小于n个,产生分支
}
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current = "";
// 特判
if (n == 0) {
return result;
}
dfs(result, current, 0, 0, n);
return result;
}
};
int main()
{
int n;
cin >> n;
int sum = 0;
vector<string>res;
Solution s;
res = s.generateParenthesis(n);
for (string c : res) {
sum++;
}
cout << sum;
return 0;
}
完全背包问题
题目描述
给定一个承重上限为w的背包,现有一系列的物品不限个数,每件物品的价值和重量分别为vi,wi。尝试使用回溯算法的思路找出该背包能装下的最大的物品价值总和。
第一行输入为两个整数,w,n,分别表示背包的承重上限和物品的类别数。
接下来的n行,每行为两个整数vi,wi分别表示第i件物品的价值和重量。
提示:
区别于01背包问题,这里的物品可以重复选择。
样例输入输出
样例1
输入:
10 4
1 2
3 3
5 4
9 7输出:
12
样例2
输入:
10 2
1 5
2 2输出:
10
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1000;
int f[N][N], v[N], w[N];
int main() {
int n, m, i, j, k;
cin >>m>>n;
for (i = 1; i <= n; i++) cin >> v[i] >> w[i]; // 价值和重量
for (i = 1; i <= n; i++) // 前i类
for (j = 1; j <= m; j++) // 重量
for (k = 0; k * w[i] <= j; k++) // 第i类物品被重量允许的数量
f[i][j] = max(f[i][j], f[i - 1][j - k * w[i]] + k * v[i]);
cout << f[n][m];
return 0;
}
圆排列问题
题目描述
给定n个圆的半径,将它们放到矩形框中,各圆与矩形底边相切,试用回溯算法求最小排列长度的圆排列的长度(结果保留两位小数)。
输入的第一行为1个整数,n,表述圆的数量
输入的第二行为n个整数,表示n个圆的半径序列。
样例输入输出
样例1
输入:
3
1 2 1输出:
7.66
样例2
输入:
4
2 4 1 1输出:
13.83
#include<iostream>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;
const int maxn = 100;
int n;
double minl = 9999;//最小排列长度
double x[maxn];//圆心横坐标
double r[maxn];//圆半径
double best[maxn];//最小圆排列的半径顺序
double center(int t)//计算每个圆心坐标
{
double len = 0;
for (int i = 1; i < t; ++i) {
double X = x[i] + 2.0 * sqrt(r[t] * r[i]);//计算圆横坐标
if (X > len)
len = X;
}
return len;
}
void compute()//根据每种排列计算其对应的长度
{
double left = 0, right = 0;
for (int i = 1; i < n; ++i) {
if (x[i] - r[i] < left)
left = x[i] - r[i];
if (x[i] + r[i] > right)
right = x[i] + r[i];
}
if (right - left < minl) {
minl = right - left; //更新最小排列长度
for (int i = 1; i < n; ++i)
best[i] = r[i]; //记录最小圆排列的半径顺序
}
}
void backtracking(int t)//回溯过程构造出排列树
{
if (t == n)
compute();
else {
//计算当前最优排列长度
for (int i = t; i < n; ++i) {
swap(r[t], r[i]);
double centerX = center(t);//得到t圆心的x坐标
if (centerX + r[t] + r[1] < minl) {
x[t] = centerX;
backtracking(t + 1);
}
swap(r[t], r[i]);//回溯,下一种排列
}
}
}
int main() {
cin >> n;
n += 1;
for (int i = 1; i < n; ++i)
cin >> r[i];
backtracking(1);
cout <<fixed<<setprecision(2)<< minl << endl;
return 0;
}