实验一 归并排序求逆序对
原理:归并排序是采用分治的方法,均摊了每一段的时间复杂度
时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),空间复杂度为
O
(
n
)
O(n)
O(n)
原理在装进新数组的时候记录逆序对的个数
比如当
a
[
i
]
>
a
[
j
]
a[i]>a[j]
a[i]>a[j] 分界点为
m
i
d
mid
mid 那么这一段的逆序对数量
为
m
i
d
−
i
+
1
mid-i+1
mid−i+1
结果:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+100;
long long ans=0;// 记录逆序对
long long a[N];
long long b[N];//相当于桶
int n;
struct node{ //记录逆序对用
int l,r;
bool operator <(const node &A){
return l<A.l||(l==A.l&&r<A.r);
}
}A[N],B[N];
int atot,btot;
//归并排序求逆序对
void mergesort(int l,int r){
if(r-l<1) return;
int mid=(l+r)>>1; //砍成两半
mergesort(l,mid); //分治
mergesort(mid+1,r);
int i=l,j=mid+1;
for(int k=l;k<=r;k++){
if(j>r||a[i]<=a[j]&&i<=mid) b[k]=a[i++];
else {
for(int z=i;z<=mid;z++) A[++atot]={a[z],a[j]};
b[k]=a[j++],ans+=mid-i+1;//记录逆序对
}
}
for(int k=l;k<=r;k++) a[k]=b[k];
}
//暴力检验,暴力求逆序对
int cnt=0;
void bf(int n){
int c[N];
cnt=0;
memcpy(c,a,sizeof(c));
for(int i=1;i<=n-1;i++)
for(int j=i+1;j<=n;j++)
if(a[i]>a[j]) {
B[++btot]={a[i],a[j]};
cnt++;
}
}
int main (){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
ans=0;
atot=btot=0;
bf(n); //暴力
mergesort(1,n);
sort(A+1,A+1+atot);
sort(B+1,B+1+btot);
cout<<endl<<"两边结果已经按照逆序对第一个数排好序:\n";
printf("\n归并排序结果: 暴力枚举检验: \n");
for(int i=1;i<=atot;i++){
printf("[%d %d]",A[i].l,A[i].r);
cout<<" ";
printf("[%d %d]\n",B[i].l,B[i].r);
}
printf("总计:%d %d\n",ans,cnt);
return 0;
}
/*
test
11
23 13 35 6 19 50 28 38 26 17 45
*/
实验二 折半查找区间
给定一个区间[a,b]。在一个升序序列中,使用减治法,设计一个改良版的折半查找算法,给出区间内的所有元素。
思路:首先序列时非严格单调递增的策略,采用分治的策略,对区间对半划分,若区间在给定范围之内直接输出结果,若不在范围之内则继续对半划分进行递归
结果:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
int a[N];
int n;
void solve(int l,int r,int L,int R){
if(L<=a[l]&&a[r]<=R){ //在判定范围之内直接输出并且结束递归
for(int i=l;i<=r;i++)
cout<<a[i]<<" ";
return;
}
if(l>=r) return; // 只有一个数,直接返回
int mid=(l+r)>>1; //取中间的值
if(L<=a[mid])solve(l,mid,L,R); //向左递归
if(R>a[mid]) solve(mid+1,r,L,R); //向右边递归
}
int main(){
cout<<"输入元素个数:";
cin>>n;
cout<<"输入元素:";
for(int i=1;i<=n;i++){
cin>>a[i];
}
int m;
cout<<"输入操作次数:";
cin>>m;
for(int i=1;i<=m;i++){
int L,R;
cin>>L>>R;
printf("在区间[%d %d]的数为:",L,R);
solve(1,n,L,R);
cout<<endl;
}
return 0;
}
/*
10
2 4 6 8 11 13 15 17 19 20
4
4 15
7 18
14 25
25 30
*/
实验三 货币支付(动态规划,完全背包问题)
其实就是个完全背包模型
设
f
(
i
,
j
)
f(i,j)
f(i,j) 表示前i种货币凑出j价值的最小货币数
当第i种货币面值高于
j
j
j面值时,说明只能继承前一种货币的最小货币数
转移状态方程有:
f
(
i
,
j
)
=
f
(
i
−
1
,
j
)
f(i,j)=f(i-1,j)
f(i,j)=f(i−1,j)
当第i种货币面值低于于
j
j
j面值时。
当前j的面值可以由
j
−
v
[
i
]
j-v[i]
j−v[i]的面值凑过来,或者继承第
i
−
1
i-1
i−1种 的货币数,他们两个取最小即可
转移状态方程
f
(
i
,
j
)
=
m
i
n
{
f
(
i
−
1
,
j
)
,
f
(
i
,
j
−
v
(
i
)
)
+
1
}
f(i,j)=min\{f(i-1,j),f(i,j-v(i))+1\}
f(i,j)=min{f(i−1,j),f(i,j−v(i))+1}
code:
#include<bits/stdc++.h>
using namespace std;
const int N=10000;
int f[N];
//int f[100][N];
int v[100];
int path[50][N];
int main(){
int n;
cout<<"输入货币种类数:";
cin>>n;
cout<<"输入货币价值:";
for(int i=1;i<=n;i++)
cin>>v[i];
memset(f,0x3f,sizeof(f));
/*
f[0][0]=0;//0 元不用凑数,所以最少为0
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++){
if(j<v[i]) f[i][j]=f[i-1][j]; //当前第i种货币价值比你要的面值高
else{
//
f[i][j]=min(f[i][j-v[i]]+1,f[i-1][j]);
}
}
*/
//我们发现每次转化都是由前一个转化而来
//所以可以压缩种类数,使得空间复杂度降为O(m)
//面值倒序凑数就行了
f[0]=0;
for(int i=1;i<=n;i++){
for(int j=v[i];j<=1000;j++)
if(f[j]>f[j-v[i]]+1){
f[j]=f[j-v[i]]+1;
path[i][j]=1; //记录路径
}
}
while(1){
printf("输入要凑的面值:");
int m;
cin>>m;
cout<<"路径:"<<endl;
int a=n,b=m;
while(a>=0&&b>=0){
if(path[a][b]){
printf("%d ",v[a]); //路径输出
b-=v[a];
}
else a--;
}
cout<<"\n最少次数:"<<f[m]<<endl;
}
return 0;
}
/*
test:
4
1 2 5 10
167
20
66
*/
实验四 汽车加油(贪心)
一种贪心策略就是我尽量跑远,那么加油的次数就会减少
算法描述:如果当前能跑的公里数大于加油站之间的距离
那么就不加油
否则就加油
之间扫描一下数组就可以了,时间复杂度为O(N)
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int a[N];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>a[i];
int tmp=n;
int cnt=0;
for(int i=0;i<=m;i++){
if(tmp>=a[i]){
tmp-=a[i];
}
else{
tmp=n;
tmp-=a[i];
cout<<"编号:"<<i-1<<endl;
cnt++;
}
}
cout<<"加油次数:"<<cnt<<endl;
return 0;
}
/*
7 7
1 2 3 4 5 1 6 6
*/
实验五 作业调度(dfs)
采用深度遍历枚举作业次序,当作业全部完成则记录时间与调度次序,如果当前方案的时间高于最优方案,我们进行可行性剪枝
具体做法递归+回溯
code:
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int a[N];
int n,m;
int work[N][4]; //工作
int step[N]; //调度方案
int pos[N]; //当前位置
int ans=(1<<30); //先将结果置于无穷大
void dfs(int d,int tim1,int tim2,int res){ //深度,工厂1的时间 工厂2的时间 工厂3的时间(最终结果)
if(res>ans) return;//大于当前最优的结果退出,剪枝
if(d>n){
if(res<ans){
for(int i=1;i<=n;i++) step[i]=pos[i]; //记录调度方案
ans=res; //记录当前最小时间
}
return; //记得返回
}
for(int i=d; i<=n; i++) {
int x=tim1,y=tim2,z=res;
tim1+=work[pos[i]][1];
tim2=(tim1>tim2?tim1:tim2)+work[pos[i]][2];
res=(tim2>res?tim2:res)+work[pos[i]][3];
//交换两个作业的位置,把选择出的原来在pos[i]位置上的任务调到当前执行的位置pos[d]
swap(pos[d],pos[i]);
dfs(d+1,tim1,tim2,res);
swap(pos[d],pos[i]);
tim1=x,tim2=y,res=z;//回溯
}
}
int main(){
cin>>n;// 几个作业
for(int j=1;j<=3;j++)
for(int i=1;i<=n;i++) cin>>work[i][j];
for(int i=1;i<=n;i++) pos[i]=i;
dfs(1,0,0,0);
cout<<"最短时间:"<<ans<<endl;
cout<<"作业调度方案:";
for(int i=1;i<=n;i++)
cout<<" "<<step[i];
cout<<endl;
}
/*
test:
4
3 5 2 6
4 2 5 1
2 1 2 1
4
5 3 2 6
4 2 5 1
2 1 2 1
4
5 3 2 1
4 2 5 1
2 1 2 1
*/
实验六 机器组装设计
机器组装(bfs)
采用广度搜索的思想,对零件各个部分进行搜索,当为第n+1个零件时,结束当前搜索,更新当前最优的结果,对于部分搜索,低于当前最优的结果直接剪枝。
最坏的时间复杂度为O(m^n)
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
const int INF=0x3f3f3f3f;
int val[N][N],weight[N][N];
struct node{
int pos,val,weight;//位置,累积价格,累积重量
string ways;//记录路径
}ans;
int n,m,d;
void bfs(){
queue<node> q;//声明队列
ans={0,INF,INF,""}; //先将目标设为价格无穷大,重量无穷大
q.push({1,0,0});
while(!q.empty()){
node no=q.front();q.pop();
int pos=no.pos,v=no.val,w=no.weight;
if(pos==n+1){
//如果重量更轻或者等重量价格更低则更新最优解
if(no.weight<ans.weight||no.weight==ans.weight&&no.val<ans.val)
ans=no;
continue;
}
for(int i=1;i<=m;i++){
int nv=v+val[pos][i]; //选择这个零件后当前
int nw=w+weight[pos][i];
if(nv>d||nw>ans.weight) continue; //当前超过价钱d或者重量超过当前最优解 剪枝
char ch=i+'0';//当前路径
q.push({pos+1,nv,nw,no.ways+ch});
}
}
}
int main(){
cin>>n>>m>>d;
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
cin>>val[i][j];
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
cin>>weight[i][j];
bfs();
cout<<"总重量"<<ans.weight<<" 总价格:"<<ans.val<<endl;
string ways=ans.ways;
for(int i=0;i<ways.size();i++){
cout<<"第"<<i+1<<"零件在"<<ways[i]<<"供应商买"<<" 价格:"<<val[i+1][ways[i]-'0']<<" 重量:"<<weight[i+1][ways[i]-'0']<<endl;
}
return 0;
}
/*
测试数据
4 3 28
9 7 5
10 8 7
5 8 9
4 7 5
3 2 1
2 1 1
1 2 2
1 2 2
*/