算法基础与在线实践
一 简单计算与模拟问题
1 装箱问题
//联系现实中尽可能装满箱子才用最少箱子 所以先大的后小的例如3*3中 先填了2*2的再填1*1的这样就可以装满并使箱子用的最少
#define _CRT_SECURE_NO_WARNINGS 1;
#include<stdio.h>
int main()
{
int a, b, c, d, e, f, ans = 0, x=0, y=0, i, j;//x和y分别为1*1 和2*2的数量
int u[4] = { 0,5,3,1 };//在一个6*6箱子放多少个3*3的箱子对应剩余多少个2*2箱子
while (scanf("%d%d%d%d%d%d", &a, &b, &c, &d, &e, &f) != EOF)
{
if (a + b + c + d + e + f == 0)
break;
ans= f + e + d + (c+3)/4;//6*6 5* 5 4*4 有多少开多少个箱子 3*3就向上取整个箱子
y += 5 * d + u[c % 4];//在4*4和3*3都有2*2的箱子空余数
if (b > y)//看题目要的箱子数和装箱剩余2*2箱子比较
{
ans += (b - y + 8) / 9;//去掉装箱剩余的 再加向上取整个箱子
}
x += 36 * ans - 36 * f - 25 * e - 16 * d - 9 * c - 4 * b;//最妙的你自己想吧
if (a > x)
ans += (a - x + 35) / 36;//同2*2判断
printf("%d\n", ans);
}
return 0;
}
2 约瑟夫问题
#include<stdio.h>
//模拟 用笔计算时会知道有一个变量需要循环从头到尾 从尾到头 还需要一个变量确定是否到了要删除这个数
//用数组存放数字
int a[305];
void init()//每次进行新一轮计算时需要初始化数组全为0即数字全都在 1表示数字被删
{
for(int i=0;i<305;i++)
{
a[i]=0;
}
}
int main()
{
int n,m,i,index,count; //index就是循环从头到尾重复循环的变量 count计算是否到删数的变量
scanf("%d%d",&n,&m);
while(n+m)
{
index=-1;//初始化为-1是因为我们从数组下标为0开始算数 等下+1正好对齐
count=0;
init();
for(int i=1;i<n;i++)
{
count=0;//每删一个数需要重置为0
while(count!=m)
{
index=(index+1)%n;//循环的方法 到加到n就从头开始
if(a[index]==0)//如果该数字存在则count+1
{
count++;
if(count==m)//如果count到m了就要删数字蜡
{
a[index]=1;//删除下标为index的数
}
}
}
}
for(int i=0;i<n;i++)//找到0-n中唯一存在的数
{
if(a[i]==0)
printf("%d\n",i+1);//这里需要+1 因为下标从0开始
}
scanf("%d%d",&n,&m);
}
return 0;
}
3排列问题
//给出一串数字求下k个方法计算每次算下一个是什么事最大就循环到最小的知道第k1个
//怎么挑选下一个就是先找到比当前数字大则先找到这串数字最高位不是1的最小数字
//213 中下一个 最高位是2 不是1 则不动2 其余最小的是1 把1放最低位中间的 从左到右升序计算最大中最小的
#include<stdio.h>
int a[1030]={0};
void nextstep(int size)//求下一个是什么数字
{
int flag=size-1;// 标记当前数字中最小的是那个下标
int tmp;
while(a[flag-1]>a[flag]&&flag!=0)//找最小的小标这里是用flag-1才是最小小标 flag是后一个 ①
{
flag--;
}
if(flag==0)//如果前面的数都比后面的数大则到了最大数了需要重新开始
{
for(int i=0;i<size;i++)
a[i]=i+1;
return ;//这里要回去这样才维护了nextstep的含义一次只找一个
}
for(int i=size-1;i>=flag;i--)//把最低位数字和最flag-1最小的数交换
{
if(a[i]>a[flag-1])
{
tmp=a[i];
a[i]=a[flag-1];
a[flag-1]=tmp;
break;
}
}
while(size-1>flag)//把flag-1 到最低位的所有数进行从左到右的升序 为什么他们一开始从左到右是降序是因为①式哪里已经确保他是降序的
{
tmp=a[flag];
a[flag]=a[size-1];
a[size-1]=tmp;
flag++;
size--;
}
}
int main()
{
int n,m,k;
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<k;i++)
nextstep(n);
for(int i=0;i<n;i++)
printf("%d ",a[i]);
printf("\n");
}
return 0;
}
第2章练习题之合唱组合
//转化为求最长上升和下降序列 总长度-去两者最大之和-1(中间的数数了两次)即为要去掉的最小个数
#include<iostream>
using namespace std;
//合唱队形
int a[10010]={0};
int b[10010]={0};
int c[10010]={0};
int main()
{
int n,k,ans,maxn;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
//第i个位置的最长上升序列
b[1]=1;
for(int i=2;i<=n;i++)
{
maxn=0;
for(int j=1;j<i;j++)
{
if(a[i]>a[j])
{
if(b[j]>maxn)
maxn=b[j];
}
}
b[i]=maxn+1;
}
//第i个位置最长下降序列
c[n]=1;
for(int i=n-1;i>=1;i--)
{
maxn=0;
for(int j=i+1;j<=0;j++)
{
if(a[i]>a[j])
{
if(c[j]>maxn)
maxn=c[j];
}
}
c[i]=maxn+1;
}
for(int i=1;i<=n;i++)
{
if(b[i]+c[i]>ans)
ans=b[i]+c[i];
}
cout<<n-(ans-1);
return 0;
}