算法设计与分析复习知识
归并排序
#include<iostream>
using namespace std;
void Marge(int a[],int b[],int s,int m,int e)
{
int i = s,j = m,k = s;
while (i < m && j < e)
{
if (a[i] <= a[j])
b[k++] = a[i++];
else
b[k++] = a[j++];
}
while (i < m)
b[k++] = a[i++];
while (j < e)
b[k++] = a[j++];
}
void Margesort(int a[],int b[],int start,int end)
{
if(start < end)
{
// mid变量的作用域仅限于if语句块内,每次递归调用都会使用正确的mid值,若在外部定义则会出现在递归调用中保留的情况,可能出现错误
int mid = start + (end - start)/2;
Margesort(a, b, start, mid);
Margesort(a, b, mid + 1, end);
Marge(a, b, start, mid, end);
for (int i = start; i < end; ++i)
{
a[i] = b[i];
}
}
}
int main()
{
int n;
cin >> n;
int r[n],b[n];
for(int i=0;i<n;i++)
{
cin >> r[i];
}
Margesort(r,b,0,n);
for(int i=0;i<n;i++)
{
cout << r[i] << " ";
}
return 0;
}
快速排序
int Partition(int a[], int low, int high) {
int pivot = a[low];
while(low < high)
{
while(low < high && a[high] >= pivot)
{
high--;
}
a[low] = a[high];
while(low < high && a[low] <= pivot)
{
low++;
}
a[high] = a[low];
}
a[low] = pivot;
return low;
}
void QuickSort(int r[], int low, int high) {
if (low < high) {
int pivot = Partition(r, low, high);
QuickSort(r, low, pivot - 1);
QuickSort(r, pivot + 1, high);
}
else
return;
}
int main() {
int n;
cin >> n;
int r[n+1];
for (int i = 1; i <= n; i++) {
cin >> r[i];
}
r[0] = 0;
QuickSort(r, 0, n - 1);
for (int i = 1; i <= n; i++) {
cout << r[i] << ' ';
}
cout << endl;
return 0;
}
堆排序
#include<iostream>
using namespace std;
void HeapAD(int R[],int i,int n)
{
R[0] = R[i];
for(int j=2*i;j<=n;j=2*j)
{
if(j < n && R[j] < R[j+1]) // 判断n/2处的根节点左右孩子谁更大,谁大选谁
j++;
if(R[0] >= R[j])
break;
else
{ // 交换孩子与根节点值
R[i] = R[j];
i = j;
}
}
R[i] = R[0];
}
void HeapSort(int r[],int n)
{
// 构造大根堆
for(int i=n/2;i>0;i--)
{
HeapAD(r,i,n);
}
// 实现堆排序
for(int i=n;i>1;i--)
{
int tmp = r[i];
r[i] = r[1];
r[1] = tmp;
HeapAD(r,1,i-1);
}
}
int main() {
int n;
cin >> n;
int r[n+1];
for (int i = 1; i <= n; i++) {
cin >> r[i];
}
r[0] = 0;
HeapSort(r,n);
for (int i = 1; i <= n; i++) {
cout << r[i] << ' ';
}
cout << endl;
return 0;
}
活动安排问题
#include <iostream>
using namespace std;
template<class Type>
void GreedySelector(int n, Type s[], Type f[], bool A[]);
const int N = 11;
int main()
{
//下标从1开始,存储活动开始时间
int s[] = {0,1,3,0,5,3,5,6,8,8,2,12};
//下标从1开始,存储活动结束时间
int f[] = {0,4,5,6,7,8,9,10,11,12,13,14};
bool A[N+1];
cout<<"各活动的开始时间,结束时间分别为:"<<endl;
for(int i=1;i<=N;i++)
{
cout<<"["<<i<<"]:"<<"("<<s[i]<<","<<f[i]<<")"<<endl;
}
GreedySelector(N,s,f,A);
cout<<"最大相容活动子集为:"<<endl;
for(int i=1;i<=N;i++)
{
if(A[i]){
cout<<"["<<i<<"]:"<<"("<<s[i]<<","<<f[i]<<")"<<endl;
}
}
return 0;
}
template<class Type>
void GreedySelector(int n, Type s[], Type f[], bool A[])
{
A[1]=true;
int j=1;//记录最近一次加入A中的活动
for (int i=2;i<=n;i++)//依次检查活动i是否与当前已选择的活动相容
{
if (s[i]>=f[j])
{
A[i]=true;
j=i;
}
else
{
A[i]=false;
}
}
}
动态规划斐波那契
//此处采用动态规划的方法进行
//即从底自上运算,空间和时间消耗非常客观
void fib(int n)
{
int f[n+1];
f[1] = 1;
f[2] = 1;
for(int i = 3;i <= n;i++)
{
f[i] = f[i-1] + f[i-2];
}
cout << "fib数列为:" << f[n] << endl;
}
int main()
{
int a;
cout << "请输入一个整数:" << endl;
cin >> a;
fib(a);//调用fib函数进行运算
}
0/1背包问题
#include<iostream>
#include<algorithm>
using namespace std;
struct Goods{
int w,v;
};
int main()
{
int n,W;
cin >> n >> W;
int f[100][100];
Goods goods[n];
for(int i=0;i<n;i++)
{
cin >> goods[i].w >> goods[i].v;
}
for (int i = 0; i <= n; ++i)
{
for (int j = 0; j <= W; ++j)
{
f[i][j] = 0;
}
}
// 对于动态递归,可使用下标为1的数组,便于管理三种状态(待装价值,当前价值,先前价值)。否则会出现数组越界现象
for(int i=1;i<=n;i++)
{
for(int j=1;j<=W;j++)
{
if(j < goods[i].w)
f[i][j] = f[i-1][j];
else
{
f[i][j] = max(f[i-1][j],f[i-1][j-goods[i].w] + goods[i].v);
}
}
}
cout << f[n][W] << endl;
return 0;
}
网格最短路径问题
#include <iostream>
using namespace std;
int MinPath(int a[3][3],int m,int n){
int dist[m][n],path[m][n];//dist存最短路径长度,path存决策.1向下走.0向右走
dist[0][0] = a[0][0];
path[0][0] = 0;
int i,j;
for(i=1;i<n;i++)
{
dist[0][i] = dist[0][i-1] + a[0][i];
path[0][i] = 1;
}
for(j=1;j<m;j++)
{
dist[j][0] = dist[j-1][0] + a[j][0];
path[j][0] = 0;
}
for(j=1;j<m;j++)
{
for(i=1;i<n;i++)
{
if(dist[j][i-1] < dist[j-1][i])
{
dist[j][i] = dist[j][i-1] + a[j][i];
path[j][i] = 1;
}
else
{
dist[j][i] = dist[j-1][i] + a[j][i];
path[j][i] = 0;
}
}
}
cout<<"回溯路径为:";
for(i=m-1,j=n-1;i>0||j>0;){
cout<<a[i][j]<<"<--";
if(path[i][j] == 0) i--;
else j--;
}
cout<<a[0][0]<<endl;
return dist[m-1][n-1];
}
int main(){
int a[3][3] = {1,3,1,1,5,1,4,2,1};
int dist = MinPath(a,3,3);
cout<<"从左上角到右下角的最短路径为:"<<dist<<endl;
return 0;
}
TSP旅行商问题
#include<iostream>
#include<cmath>
using namespace std;
#define N 9999
//TSP问题求解函数
void TSP(int n,int** graph,int location){
//构建二维数组[i,2^n],j模拟集合个数
int **D = new int*[n+1]; //建行0~n,为方便理解和表示,第0行未使用
int **Rec = new int*[n+1]; //建立Rec
for(int i=1;i<=n;i++){
//建列表示集合
D[i] = new int [(int)pow(2,n)];
Rec[i] = new int [(int)pow(2,n)];
}
//初始化D、Rec
for(int i=1;i<=n;i++){
D[i][0]=graph[i][location]; //D[i,{}]
Rec[i][0]= -1;
for(int j=1;j<=(int)pow(2,n)-1;j++){
D[i][j]=N;
Rec[i][j] = -1;
}
}
//动态规划
for(int j=1;j<=(int)pow(2,n)-1;j++){ //对每一列求解
for(int i=1;i<=n;i++){ //每一行找最短路径
int min = N;
if(((int)pow(2,i-1) & j) == 0){ //顶点集不能包含i
int length = N;
for(int k=1;k<=n;k++){
if((int)pow(2,k-1) & j ){ //若顶点在集合中
length = graph[i][k] + D[k][j-(int)pow(2,k-1)];
if(length < min){
min = length;
D[i][j] = min;
Rec[i][j] = k;//局部最优决策
}
}
}
}
}
}
/*
(int)pow(2,n)-1 表示所有顶点都被访问过一次时的状态。
确保顶点 i 不在顶点集合,而(int)pow(2,location-1)表示只有起始顶点被访问的状态
需要将顶点 j 中已经包含的顶点 i 剔除掉
*/
cout<<"最短长度:"<<D[location][(int)pow(2,n)-1-(int)pow(2,location-1)]<<endl;//最短路径长度
cout<<"最短路径为:"<<location;
int row = location;
int column = (int)pow(2,n)-1-(int)pow(2,row-1);
while(column > 0){
cout<< "->"<<Rec[row][column];
row = Rec[row][column];
column -= (int)pow(2,row-1);
}
cout<<"->"<<location<<endl;
}
int main(){
cout<<"旅行家需要游历多少个城市?:"<<endl;
int n;
cin>>n;
//建立二维数组模拟邻接矩阵
int **graph=new int* [n+1];
for(int i=1;i<=n;i++)
graph[i] = new int[n+1];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<"输入邻接矩阵graph["<<i<<"]["<<j<<"]的权:"<<endl;
cin>>graph[i][j];
}
}
cout<<"旅行家现在在第几个城市?"<<endl;
int location;
cin>>location;
TSP(n,graph,location); //TSP求解
return 0;
}
最长递增子序列问题
#include <iostream>
using namespace std;
int str[999];
int L[999];
int a[999][999];
int main()
{
int n;
cin >> n;
int i,j,k;
int count;
int max;
for(i=0;i<n;i++)
{
cin >> str[i];
}
for(i=0;i<n;i++)
{
L[i] = 1;
a[i][0] = str[i];
}
for(i=1;i<n;i++)
{
max = 1;
for(j=i-1;j>=0;j--)
{
if((str[i] > str[j]) && (max < L[j]+1))
{ // 这个判断条件保证了最长子序列的递增性
max = L[j] + 1;
L[i] = max;
for(k=0;k<max-1;k++)
{
a[i][k] = a[j][k];
}
}
}
a[i][max - 1] = str[i];
}
for(count=0,i=1;i<n;i++)
{
if(L[count] < L[i])
count = i;
}
cout<<"最长递增子序列是:";
for (i=0; i<L[count]; i++)
cout<<a[count][i]<<" ";
return L[count];
}
int a[999][999]
int v[999],w[999]
int main()
{
int n,v;
int i,j;
cin >> n >> v;
for(i=1;i<=n;i++)
{
cin >> v[i] >> w[i];
}
for(i=1;i<=n;i++)
{
for(j=1;j<=v;j++)
{
if(j<v[i])
{
a[i][j] = a[i-1][j];
}
else
{
a[i][j] = max(a[i-1][j],a[i-1][j-v[i]]+w[i])
}
}
}
cout << a[n][v];
return 0;
}
最长子序列问题
#include <iostream>
#include <string>
#include <stack>
using namespace std;
void LCS(string s1,string s2)
{
int m=s1.length()+1;
int n=s2.length()+1;
int **c;
int **b;
c=new int* [m];
b=new int* [m];
for(int i=0;i<m;i++)
{
c[i]=new int [n];
b[i]=new int [n];
for(int j=0;j<n;j++)
b[i][j]=0;
}
for(int i=0;i<m;i++)
c[i][0]=0;
for(int i=0;i<n;i++)
c[0][i]=0;
for(int i=0;i<m-1;i++)
{
for(int j=0;j<n-1;j++)
{
if(s1[i]==s2[j])
{
c[i+1][j+1]=c[i][j]+1;
b[i+1][j+1]=1; //1表示箭头为 左上
}
else if(c[i][j+1]>=c[i+1][j])
{
c[i+1][j+1]=c[i][j+1];
b[i+1][j+1]=2; //2表示箭头向 上
}
else
{
c[i+1][j+1]=c[i+1][j];
b[i+1][j+1]=3; //3表示箭头向 左
}
}
}
for(int i=0;i<m;i++) //输出c数组
{
for(int j=0;j<n;j++)
{
cout<<c[i][j]<<' ';
}
cout<<endl;
}
/*
用于判断重复字段的元素
*/
stack<char> same; //存LCS字符
stack<int> same1,same2; //存LCS字符在字符串1和字符串2中对应的下标,方便显示出来
for(int i = m-1,j = n-1;i >= 0 && j >= 0; )
{
if(b[i][j] == 1)
{
i--;
j--;
same.push(s1[i]);
same1.push(i);
same2.push(j);
}
else if(b[i][j] == 2)
i--;
else
j--;
}
cout<<s1<<endl; //输出字符串1
for(int i=0;i<m && !same1.empty();i++) //输出字符串1的标记
{
if(i==same1.top())
{
cout<<1;
same1.pop();
}
else
cout<<' ';
}
cout<<endl<<s2<<endl; //输出字符串2
for(int i=0;i<n && !same2.empty();i++) //输出字符串2的标记
{
if(i==same2.top())
{
cout<<1;
same2.pop();
}
else
cout<<' ';
}
cout<<endl<<"最长公共子序列为:";
while(!same.empty())
{
cout<<same.top();
same.pop();
}
cout<<endl<<"最长公共子序列长度为:"<<c[m-1][n-1]<<endl;
for (int i = 0; i<m; i++)
{
delete [] c[i];
delete [] b[i];
}
delete []c;
delete []b;
}
int main()
{
string s1="ABCPDSFJGODIHJOFDIUSHGD";
string s2="OSDIHGKODGHBLKSJBHKAGHI";
LCS(s1,s2);
return 0;
}
最优二叉树搜索问题
#include <iostream>
#define MAXNUM 100
#define MAX 65536
using namespace std;
//p中为有序关键字k1到k5的搜索概率,k1<k2<k3<k4<k5
double p[MAXNUM] = {0.00, 0.15, 0.10, 0.05, 0.10, 0.20};
double q[MAXNUM] = {0.05, 0.10, 0.05, 0.05, 0.05, 0.10};
void optimal_bst(double e[][MAXNUM], int root[][MAXNUM], double w[][MAXNUM], int n)
{
int i = 0, j = 0;
//针对左或右孩子为空树情况初始化
for(i = 1; i <= n + 1; i++)
{
e[i][i - 1] = q[i - 1];
w[i][i - 1] = q[i - 1];
}
int l = 0;
/*计算顺序如下:根据计算式:e[i,j] = e[i,r-1]+e[r+1,j
首先计算节点个数为1的最优二叉树的代价e[1,1],e[2,2]……
接着计算节点个数为1的最优二叉树的代价e[1,2],e[2,3]……
……
最后计算结点个数为n的最优二叉树的代价e[1,n],利用之前保存的较少结点最优二叉树的结果.*/
for(l = 1; l <= n; l++)
{// 外层循环 l 表示当前考虑的节点范围的大小,从 1 到 n。
for(i = 1; i <= n - l + 1; i++)
{// 内层循环 i 和 j 表示当前考虑的子树的起始节点和结束节点。
j = i + l - 1;
e[i][j] = MAX;
w[i][j] = w[i][j - 1] + p[j] + q[j];
for(int r = i; r <= j; r++)
{
double t = 0;
t = e[i][r - 1] + e[r + 1][j] + w[i][j];
if(t < e[i][j])
{
e[i][j] = t;
root[i][j] = t;
}
}
}
}
}
int main()
{
double e[MAXNUM][MAXNUM];
int root[MAXNUM][MAXNUM];
double w[MAXNUM][MAXNUM];
optimal_bst(e, root, w, 5);
for(int i = 1; i <= 6; i++)
{
for(int j = 0; j <= 5; j++)
{
cout << e[i][j] << " ";
}
cout << endl;
}
}
近似串匹配问题
#include<iostream>
using namespace std;
int D[20][20]; // 填表矩阵设置为全局变量
int min(int a,int b,int c)
{
if(a<b && a<c) return a;
else if(b<a && b<c) return b;
else return c;
}
// 返回 P和 T的最小差别数
int ASM(char P[],int m,char T[],int n)
{
// 初始化第 0行
for(int j=1;j<=n;j++) D[0][j] = j;
// 初始化第 0列
for(int i=1;i<=m;i++) D[i][0] = i;
// 根据递推式依次计算每一列
for(int j=1;j<=n;j++){
for(int i=1;i<=m;i++){
// 注意数组下标从 0开始,即P[i-1]对应模式P的第i个元素,T[j-1]为文本T的第j个元素
if(P[i-1]==T[j-1])
D[i][j] = min(D[i-1][j-1],D[i-1][j]+1,D[i][j-1]+1);
else
D[i][j] = min(D[i-1][j-1]+1,D[i-1][j]+1,D[i][j-1]+1);
}
}
return D[m][n];
}
int main()
{
char P[5] = {'h','a','p','p','y'};
char T[6] = {'h','s','p','p','a','y'};
cout<<"最小差别数为:"<<ASM(P,5,T,6)<<endl;
return 0;
}
藏宝图DFS问题
#include <iostream>
#include <vector>
using namespace std;
int edge[999][999];
int visited[999];
int DFS(int u, int v, int n) {
visited[u] = 1;
if (u == v) {
return 1;
}
for (int i = 0; i < n; i++) {
if (edge[u][i] == 1 && visited[i] == 0) {
if (DFS(i, v, n) == 1) {
return 1;
}
}
}
return 0;
}
int main() {
int n, start, end;
cout << "请输入山洞数量:";
cin >> n;
for (int i = 0; i < n; i++) {
visited[i] = 0;
for (int j = 0; j < n; j++) {
edge[i][j] = 0;
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if(i == j)
edge[i][j] = 1;
else
{
cout << "请输入" << i << "山洞与" << j << "山洞之间的连通关系(0表示不连通,1表示连通):" << endl;
cin >> edge[i][j];
}
}
}
cout << "请输入寻宝图所在山洞编号:";
cin >> end;
cout << "请输入猎人起始的山洞编号:";
cin >> start;
// 调用 DFS 函数判断猎人是否能找到寻宝图
if (DFS(start, end, n) == 1) {
cout << "猎人能够找到寻宝图!" << endl;
} else {
cout << "猎人无法找到寻宝图!" << endl;
}
return 0;
}
图着色问题
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 1005;
int G[maxn][maxn], color[maxn];//用于存储图中的边--用于存储每个节点的颜色
int n, m; //n表示图中节点的数量,m表示可供选择的颜色数目。
bool ok(int u, int c) {
for (int i = 1; i <= n; i++) {
if (G[u][i] == 1 && color[i] == c) // 判断是否相邻节点已经着色 c
return false;
}
return true;
}
bool dfs(int u) {
if (u > n)
return true;
for (int i = 1; i <= m; i++) {
if (ok(u, i)) {
color[u] = i;
if (dfs(u + 1))
return true;
color[u] = 0;
}
}
return false;
}
int main() {
memset(G, 0, sizeof(G));//初始化邻接矩阵为0
memset(color, 0, sizeof(color));//初始化颜色为0
int e;//e表示图中边的数量
cin >> n >> e >> m;
for (int i = 0; i < e; i++) {
int u, v;
cin >> u >> v;
G[u][v] = G[v][u] = 1;
}
if (dfs(1)) { // 从第1个顶点开始进行递归着色
cout << "Yes" << endl;
for (int i = 1; i <= n; i++) {
cout << "结点 " << i << " 的颜色为: " << color[i] << endl;
}
}
else {
cout << "No" << endl;
}
return 0;
}
N皇后问题
#include <iostream>
#include <cmath>
using namespace std;
int N,res=0;
int queen(int arr[],int r)
{
if(r == N)
res++;
else
{
for(int c=0;c<N;c++)
{
arr[r] = c;
bool can = true;//用于标记是否可以放皇后
for(int x=0;x<r;x++)
{ //不在同一行
if(arr[r] == arr[x] || //同一列的情况
abs(r-x) == abs(arr[r] - arr[x]))//同一对角线的情况(斜率判断)
can = false;
break;
}
}
}
return res;
}
int main(){
cin >> N;
int arr[N];
cout<<queen(arr,0);
return 0;
}
批处理作业调度问题
#include <iostream>
using namespace std;
int n; //作业数
int M[100][100]; //M[i][j]表示第i个作业在机器j上需要处理的时间
int x[100]; //x[i]表示第i个处理的作业为x[i]
int bestx[100]; //x[i]的最优值
int f1; //作业在机器1上完成处理的时间
int f2[100]; //f2[i]表示第i个作业在机器2上完成处理的时间
int f; //用于记录前i个作业在机器2上完成处理的时间和
int bestf; //f的最优值
void Swap(int &a,int &b) //交换函数
{
int temp;
temp=a;
a=b;
b=temp;
}
void Backtrack(int i)
{
if(i>n) //每到达一个叶子结点,一定是产生了一个最优解,因此要更新之前最优解的值
{
if(f<bestf) //更新最优解
{
for(int j=1;j<=n;j++)
bestx[j]=x[j];
bestf=f;
}
}
else
{
for(int j=i;j<=n;j++) //控制展开 i-1 层结点的各个分支。例如当i=1时,表示在整棵排列树的根结点处,刚要开始探索结点,这时可以展开的分支有1、2、3……
{
f1+=M[x[j]][1]; //计算第 i 个作业在机器1上的完成处理的时间
if(f2[i-1]>f1) //如果第 i-1 个作业在机器2上的完成处理的时间大于第i个作业在机器1上的完成处理的时间,那么第i个作业想进入机器2,就要等第 i-1 个作业在机器2上完成后再说
f2[i]=f2[i-1]+M[x[j]][2];
else //否则,第i个作业可以在机器1上完成处理后直接进入机器2。
f2[i]=f1+M[x[j]][2];
f+=f2[i]; //计算完第i个作业在机器2上的完成处理的时间,就可以计算出前i个作业在机器2上完成处理的时间和了
if(f<bestf) //截止到这,已经得到一个前i个作业在机器2上完成处理的时间和f,如果f比之前记录的前i个作业在机器2上的完成处理的时间和的最优值bestf都要小,就可以生成第i层结点的孩子结点,继续探索下一层
{
Swap(x[i],x[j]); //把处于同一层的并且使f更小的那个结点拿过来,放到正在探索的这个结点处(这里结合排列数的图可以更好地理解)
Backtrack(i+1); //继续探索以第i层结点为根结点的子树
Swap(x[i],x[j]); //探索完要回溯时,只要做探索前的反“动作”就可以了
}
f-=f2[i]; //探索完要回溯时,只要做探索前的反“动作”就可以了
f1-=M[x[j]][1]; //探索完要回溯时,只要做探索前的反“动作”就可以了
}
}
}
void inPut() //输入函数
{
cout<<"请输入作业的个数n:"<<endl;
cin>>n;
cout<<"请分别输入n个作业在机器1和机器2上各自需要处理的时间(分两行):"<<endl;
for(int i=1;i<=2;i++)
{
for(int j=1;j<=n;j++)
cin>>M[j][i];
}
}
void initialize() //初始化函数
{
for(int i=1;i<=n;i++)
x[i]=i; //初始化当前作业调度的一种排列顺序
bestf=10000; //此问题是得到最佳作业调度方案以便使其完成处理时间和达到最小,所以当前最优值bestf应该初始化赋值为较大的一个值
}
void outPut() //输出函数
{
cout<<"这"<<n<<"个作业的最佳调度顺序为:"<<endl;
for(int i=1;i<=n;i++)
cout<<bestx[i]<<" ";
cout<<endl;
cout<<"该作业调度的完成时间和为:"<<endl;
cout<<bestf<<endl;
}
int main()
{
inPut();
initialize();
Backtrack(1);
outPut();
return 0;
}
实际案例
建筑公司铺路
某城市规划公司计划建造一条由东向西的主道路,该主道路要穿过一个有n个住宅楼的社区,从每个住宅楼都要有条辅道路沿最短路经(或南或北)与主道路相连。如果给定n个住宅楼的位置,即它们的x坐标(东西向)和y坐标(南北向),应该如何确定主道路的最优位置?即:使各住宅楼到主道路之间的辅道路长度总和最小的位置? 要求:给定n个住宅楼的位置,求解各住宅楼到主道路之间的辅道路最小长度总和。 输入(Input): (1)第1行是住宅楼数n,1≤n≤10000: (2)接下来n行是住宅楼的位置,每行2个整数x和y(空格相隔),-10000≤x,y≤10000。 输出(Output) 各住宅楼到主道路之间的辅道路最小长度总和, 示例(Sample): 输入: 5 1 2 2 2 1 3 3 -2 3 3 输出: 6 |
---|
代码实现
#include<iostream>
#include<algorithm>
#include <cmath>
using namespace std;
typedef struct Build{
int x,y; // 给定x坐标和y坐标
};
/*
sort 函数的使用:(start,end,cmp),而cmp是一个bool值,若编写cmp为返回“a < b”,则为降序排列;否则为升序排列
*/
bool cmp(Build a,Build b)
{
return a.x < b.x;
}
int main()
{
int n; // 建筑的数量
cin >> n;
Build building[n]; // 创建住宅结构体
for(int i=0;i<n;i++){
cin >> building[i].x >> building[i].y;
}
sort(building,building+n,cmp); // 按x坐标进行排序,便于创建主干道
int mainroad = building[n/2].x; // 以中间位置的建筑为中心,创建主干道
int roadlength = 0;
for(int i=0;i<n;i++){
if(building[i].x == mainroad) // 主干道上的建筑不计入铺路长度
continue;
roadlength += abs(building[i].y - building[n/2].y);
}
cout << roadlength << endl;
return 0;
}
过山车问题(0/1背包问题的变体)
设在某游乐场有n个游客 {1,2,… n}要乘坐承载重量为W过山车。第i个人的重量是wi,1≤in,确定这n个人乘坐过山车的一个方案,使得过山车能够乘坐尽可能多的人。 要求:对于给定的n个人的重量,计算过山车上乘坐最多的人数。 输入(Input)。 (1)2个正整数,分别表示人数n和过山车承载重量W; (2)n个正整数,表示人的重量(空格相隔)0 输出(Output) 最多乘坐过山车的人数。 示例(Sample): 输入(Input) 6 50 2 3 13 8 80 20 输出(Output): 5 |
---|
###代码实现
贪心法求解
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a, int b)
{
return a > b;
}
int main()
{
int n, W;
cin >> n >> W;
int count = 0;
int weights[n];
for (int i = 0; i < n; i++)
{
cin >> weights[i];
}
sort(weights, weights + n, cmp);
for (int i = 0; i < n && W != 0; i++)
{
if (weights[i] < W)
{
count++;
W -= weights[i];
}
}
cout << count << endl;
return 0;
}
动态规划法求解
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int n, W;
cin >> n >> W;
int w[n];
for (int i = 0; i < n; ++i) {
cin >> w[i];
}
int f[n + 1][W + 1];
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= W; ++j) {
f[i][j] = 0;
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= W; ++j) {
// 不选择第i个人
f[i][j] = f[i - 1][j];
// 选择第i个人
if (j >= w[i - 1]) {
f[i][j] = max(f[i][j], f[i - 1][j - w[i - 1]] + 1);
}
}
}
cout << f[n][W] << endl;
return 0;
}
北京奥森机器人(游艇租借问题变体)
北京奥林匹克森林公园北园有一批“任逍遥”伴游机器人,公园内设置了n个伴游机器人出租站1,2,…,n。游客可以在这些出租站租用伴游机器人,并在任何一个出租站归还。伴游机器人出租站i到伴游机器人出租站j之间的租金为r(i,j)(其中1≤i<j≤n) 要求:计算出从伴游机器人出租站1到伴游机器人出租站n所需要的最少租金。 输入(Input) (1)伴游机器人出租站n; (2)接下来n-1行的r(i,j)(其中1≤i<j≤n) 输出(Output) 从伴游机器人出租站1到伴游机器人出租站n所要的最少租 金 示例(Sample) 输入(Input) 3(站数) 5 15(第一站到其他相应各站的租金) 7(第二站到其他相应各站的租金) 输出(Output) 12 |
---|
代码实现
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n; // 站数
cin >> n;
int i,j,k;
// 初始化站台租金
int r[100][100];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if(i == j)
r[i][j] = 0;
else
r[i][j] = 999;
}
}
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
cin >> r[i][j];
}
}
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
for(k=i+1;k<j;k++)
{
if (r[i][k] != 999 && r[k][j] != 999)
{// 判断是否存在可走的路
r[i][j] = min(r[i][j], r[i][k] + r[k][j]); // 状态转移方程,实现求最小租金
}
}
}
}
cout << r[0][n-1];
return 0;
}