其实这类问题我觉得更多的是建模,通过实际模拟整个流程得出结果,可能会有些复杂的代码,用来解释每个过程

 

模拟退火:贪心+概率

解决:函数最值、TSP旅行商问题、最小园覆盖、最小求覆盖

模板:

//模拟退火
int eps=1e-8;  //终止温度 
int T=100;    //初始温度
double delta=0.98; //降温系数
double js(int x) ;  //评价函数
double now,next;

while(T>eps){
	js(next);
	js(now);
	de=g(next)-g(now);
	if(de>=0) now=next; //新状态更好,就受
	else if(exp(de/T)>rand()) now=next;  //新状态更差,以一定概率接受
	T*=delat; 
} 
 

hud 2899

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//模拟退火

double y;
double eps=1e-8;
double js(double x){
	return 6*pow(x,7)+8*pow(x,6.0)+7*pow(x,3)+5*pow(x,2)-y*x;
}
double solve(){
	double T=100;  //初始温度 
	double delta=0.98;
	double x=50.0;   //x的初始值 
	double now=js(x);
	double ans=now; //存储最优值 
	while(T>eps){
		int f[2]={1,-1};
		//按照概率改变x
		double nex=x+f[rand()%2]*T;
		if(nex<101&&nex>=0){
			double next=js(nex);
			ans=min(ans,next);
			if(now-next>eps){
				//新的更好
				now=next;
				x=nex; 
			}
		}
		T*=delta; //按照时间降低 
	}
	return ans; 
}


int main(){
	int tot;
	scanf("%d",&tot);
	while(tot--){
		scanf("%lf",&y);
		printf("%.4lf\n",solve());
	}
return 0;
}

 

【模拟】

这些都是《课课通》上面的:

例1、蚱蜢

【问题描述】 有一天,一只蚱蜢像往常一样在草地上愉快地跳跃,它发现了一条写满了英文字母的纸带。 蚱蜢只能在元音字母(A、E、I、O、U、Y)间跳跃,一次跳跃所需的能力是两个位置的差。纸带所需的能力值为蚱蜢从纸带开头的前一个位置根据规则跳到纸带结尾的后一个位置的过程中能力的最大值。 蚱蜢想知道跳跃纸带所需的能力值(最小)是多少。如图 9.3-1 所示的纸带所需的能力值(最小)是 4。

【输入格式】 一行一个字符串,字符串长不超过 100。 【输出格式】 一行一个整数,代表(最小)能力值。

【输入样例】 KMLPTGFHNBVCDRFGHNMBVXWSQFDCVBNHTJKLPMNFVCKMLPTGFHNBVCDRF-GHNMBVXWSQFDCVBNHTJKLPMNFVC

【输出样例】 85

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
using namespace std;
string a;

int main(){
	cin>>a;
	int ans=0,pre=0;
	for(int i=0;i<a.length();i++){
		if(a[i]=='A'||a[i]=='E'||a[i]=='I'||a[i]=='O'||a[i]=='U'||a[i]=='Y'){
			if(ans<=(i+1-pre)) ans=i+1-pre;
			pre=i+1;
		}
	}
	if(ans<=(a.length()+1-pre)) ans=a.length()+1-pre;
	cout<<ans<<endl;
return 0;
}

例2、遭遇战

【问题描述】 小林和小华在一个 n×n 的矩形方格里玩游戏,矩形左上角为(0,0),右下角为(n-1,n-1)。 两人同时进入地图的随机位置,并以相同速度进行走位。为了隐蔽性,两人都不会再走自己走过的格子。如果两人向某一方向前进,那么他们会跑到不能跑为止,当不能跑的时候,小林会向右转,小华则会向左转,如果不能跑,则不再动。现在已知两人进入地图的初始位置和方向,请算出两人遭遇的位置。 【输入格式】 第 1 行 1 个正整数 t,表示测试数据组数,1≤t≤10。 接下来的 t 组数据,每组数据的第 1 行包含 1 个整数 n,1≤n≤1000。

第 2 行包含 3 个整数 x、y 和 d,表示小林的初始位置和一开始跑的方向。其中,d=0 表示东; d=1 表示南;d=2 表示西;d=3 表示北。 第 3 行与第 2 行格式相同,但描述的是小华。

【输出格式】 输出 t 行,若会遭遇,则包含两个整数,表示他们第一次相遇格子的坐标,否则输出“-1”。

【输入样例】 2 2 0 0 0 0 1 2 4 0 1 0 3 2 0 【输出样例】 -1 1 3

设置两个布尔型数组,分别记录模拟每个人走过的格子。如果两人没有相遇并且还可以跑,就让他们按照规则一直跑下去。

#include<bits/stdc++.h>

using namespace std;
const int maxn=1010;
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};  //分别是东南西北
bool v1[maxn][maxn],v2[maxn][maxn];
void work(){
	bool c1=true,c2=true; //分别表示两个人可不可以继续走
	int n,x1,y1,d1,x2,y2,d2;
	cin>>n>>x1>>y1>>d1>>x2>>y2>>d2;
	x1++;y1++;x2++;y2++;
	memset(v1,true,sizeof(v1));
	memset(v2,true,sizeof(v2)); //让边缘变为不能走的
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++) v1[i][j]=v2[i][j]=false;
	v1[x1][y1]=true;
	v2[x2][y2]=true;
	if(x1==x2&&y1==y2) {
		cout<<"-1"<<endl;return;
	} 
	while(c1||c2){
		if(c1){
			int tx1=x1+dx[d1],ty1=y1+dy[d1];
			if(v1[tx1][ty1]){  //如果不能走,就要换方面,第一个人是默认右转!!注意这里怎么转为右转的方向的
				d1=(d1+1)&3; //位运算符 
 				tx1=x1+dx[d1];ty1=y1+dy[d1];
 				if(v1[tx1][ty1]) c1=false;
 				else {
 					x1=tx1;
					 y1=ty1;
				 }
			}
			else { x1=tx1;y1=ty1;
			}
		}
		if(c2){
			int tx2=x2+dx[d2],ty2=y2+dy[d2];
			if(v2[tx2][ty2]){
				d2=(d2+3)&3; //变为左转
				tx2=x2+dx[d2];ty2=y2+dy[d2];
				if(v2[tx2][ty2]) c2=false;
				else {
					x2=tx2;y2=ty2;
				} 
			}
			else{ x2=tx2;y2=ty2;
			}
		}
		v1[x1][y1]=true;v2[x2][y2]=true;
		if(x1==x2&&y1==y2) {
			cout<<x1-1<<" "<<y1-1<<endl;
			return;
		}
	}
	cout<<"-1"<<endl;
} 

int main(){
	int t;
	cin>>t;
	while(t--) work();
	return 0;
}

  

例3、乒乓球

【问题描述】 国际乒联立志推行一系列改革,以推动乒乓球运动在全球的普及。其中 11 分制改革引起了很大的争议,有一部分球员因为无法适应新规则只能选择退役。华华就是其中一位,他退役之后走上了乒乓球研究工作,意图弄明白 11 分制和 21 分制对选手的不同影响。在开展研究之前,他首先需要对自己多年比赛的统计数据进行一些分析,所以需要一些帮忙。 华华通过以下方式进行分析,首先将比赛每个球的胜负列成一张表,然后分别计算在 11 分制和 21 分制下,双方的比赛结果(截至记录末尾)。

比如,现在有这么一份记录,(其中 W 表示华华获得一分,L 表示华华对手获得一分): WWWWWWWWWWWWWWWWWWWWWWLW。在 11 分制下,此时比赛的结果是华华第一局 11 比 0 获胜,第二局 11 比 0 获胜,正在进行第三局,当前比分 1 比 1。而在 21 分制下,此时比赛结果是华华第一局 21 比 0 获胜,正在进行第二局,比分 2 比 1。如果一局比赛刚开始,则此时比分为 0 比 0。 本题就是要对于一系列比赛信息的输入(WL 形式),输出正确的结果。 【输入格式】 输入文件包含若干行字符串(每行至多 20 个字母),字符串由大写的 W、L 和 E 组成。其中E 表示比赛信息结束,程序应该忽略 E 之后的所有内容。

【输出格式】 输出由两部分组成,每部分有若干行,每一行对应一局比赛的比分(按比赛信息输入顺序)。 其中第一部分是 11 分制下的结果,第二部分是 21 分制下的结果,两部分之间由一个空行分隔。

【输入样例】 WWWWWWWWWWWWWWWWWWWW WWLWE

【输出样例】 11∶0 11∶0 1∶1 21∶0 2∶1

#include<bits/stdc++.h>

using namespace std;
const int maxn=10100;
string com; 
int main(){
	int a[maxn]={0},b[maxn]={0},k=0;
	int a1[maxn]={0},b1[maxn]={0},k1=0;
	while(true){
		cin>>com;
		bool flag=1; //需要在里面每一次循环进行设置
		for(int i=0;i<com.length();i++){
			if(com[i]=='E') {flag=0;break;
			}
			if(com[i]=='W'){
				a[k]++;
				if((a[k]==11&&b[k]<=9)||(a[k]>11&&b[k]<=a[k]-2)) k++;
				a1[k1]++;
				if((a1[k1]==21&&b1[k1]<=19)||(a1[k1]>21&&b1[k1]<=a1[k1]-2)) k1++;
			}
			if(com[i]=='L'){
				b[k]++;
				if((b[k]==11&&a[k]<=9)||(b[k]>11&&a[k]<=b[k]-2)) k++;
				b1[k1]++;
				if((b1[k1]==21&&a1[k1]<=19)||(b1[k1]>21&&a1[k1]<=b1[k1]-2)) k1++;
			}
		}
		if(!flag) break; 
	}
	for(int i=0;i<=k;i++) cout<<a[i]<<":"<<b[i]<<endl;
	cout<<endl;
	for(int i=0;i<=k1;i++) cout<<a1[i]<<":"<<b1[i]<<endl;
	return 0;
}

  

例4、保龄球

【问题描述】 打保龄球是用一个滚球去打击十个站立的柱,将柱击倒。一局分十轮,每轮可滚球一次或多次,以击倒的柱数为依据计分。一局得分为十轮得分之和,而每轮的得分不仅与本轮滚球情况有关,还可能与后续一两轮的滚球情况有关。即某轮某次滚球击倒的柱数不仅要计入本轮得分,还可能会计入前一两轮得分。具体的滚球击柱规则和计分方法如下: (1) 若某一轮的第一次滚球就击倒全部十个柱,则本轮不再滚球(若是第十轮则还需另加两次滚球,不妨称其为第十一轮和第十二轮,并不是所有的情况都需要滚第十一轮和第十二轮球)。该轮得分为本次击倒柱数 10 与以后两次滚球所击倒柱数之和。

( 2) 若某一轮的第一次滚球未击倒十个柱,则可对剩下未倒的柱再滚球一次。如果这两次滚球击倒全部十个柱,则本轮不再滚球(若是第十轮则还需另加一次滚球),该轮得分为这两次共击倒柱数 10 与以后一次滚球所击倒柱数之和。 (3) 若某一轮两次滚球未击倒全部十个柱,则本轮不再继续滚球,该轮得分为这两次滚球击倒的柱数之和。 总之,若某一轮中一次滚球或两次滚球击倒十个柱,则本轮得分是本轮首次滚球开始的连续三次滚球击倒柱数之和(其中有一次或两次不是本轮滚球)。若一轮内二次滚球击倒柱数不足十个,则本轮得分即为这两次击倒柱数之和。下面以实例说明如下:

轮 1 2 3 4 5 6 7 8 9 10 11 12 击球情况 / / / 72 9/ 81 8/ / 9/ / 8/ 各轮得分 30 27 19 9 18 9 20 20 20 20 累计总分 30 57 76 85 103 112 132 152 172 192 现在请编写一个保龄球计分程序,用来计算并输出最后的总得分。 【输入格式】 输入一行,为前若干轮滚球的情况,每轮滚球用一到两个字符表示,每一个字符表示一次击球,字符“/”表示击倒当前球道上的全部的柱,否则用一个数字字符表示本次滚球击倒的当前球道上的柱的数目,两轮滚球之间用一个空格隔开。

【输出格式】 输出一行一个整数,代表最后的得分。

【输入样例 1】 / / / 72 9/ 81 8/ / 9/ / 8/ 【输出样例 1】 192

【输入样例 2】 90 90 / 9/ 81 / / / 72 / /0 【输出样例 2】 170

【输入样例 3】 / / / 72 9/ 81 8/ / 9/ 15 【输出样例 3】 169

这道题必须要打草稿吧

#include<bits/stdc++.h>

using namespace std;
const int maxn=10100;
string com[13];
int t=0,score=0; //t是轮数 
int f(char c){
	if(c=='/') return 10;
	else return int(c)-int('0');
}
int fs(int x){
	string str=com[x],str1=com[min(x+1,t)],str2=com[min(x+2,t)];
	int ans=0;
	if(str[0]!='/'&&str[1]!='/') ans=f(str[0])+f(str[1]);
	else if(str[0]=='/'){
		if(str1[0]=='/'&&str1.size()==1) ans=20+f(str2[0]); //这一轮和下一轮都是10 ,就直接20+第三轮
		else ans=10+min(10,f(str1[0])+f(str1[1])); //第一轮是10,第二轮不是直接10,就要算第二轮的两次 
	}
	else ans=10+f(str1[0]); //第一轮两次中第二次才是10,则只算第二轮的第一次 
	return ans;
}
int main(){
	for(t=1;t<=10;t++) cin>>com[t];
	if(com[10][0]=='/') {
		cin>>com[11]; t++;
		if(com[11].size()==1) {
			cin>>com[12];t++; //十一轮第一次为全中 
		}
	}
	else if(com[10].size()>1){
		if(com[10][1]=='/') { cin>>com[11];t++;
		}
	} 
	for(int i=1;i<=10;i++) score+=fs(i);
	cout<<score<<endl;
	return 0;
}

很简单的题1478 -- 【分治练习】比赛安排1285

设有2^n(n<=6)个球队进行单循环比赛,计划在2^n-1天内完成,每个队每天进行一场比赛。设计一个比赛的安排,使在2^n-1天内每个队都与不同的对手比赛.例如n=2时的比赛安排为:

  队 1 2 3 4
  比赛安排为 
  第一天 1-2 3-4 
  第二天 1-3 2-4 
  第三天 1-4 2-3

题解:
is[i][j]记录i与j有没有比过,vis[i]记录当天i有没有比过,
每天都从i=1开始遍历vis[i],若i在当天还没有出现过,则
遍历is[i][j],取符合条件的最小j.

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
int vis[110],is[110][110];
int n,t;
int main(){
	scanf("%d",&n);
	t=(1<<n)-1;
	while(t--){
		memset(vis,0,sizeof(vis));  //当天有没有比赛过
		bool ok=0;
		for(int i=1;i<=(1<<n);i++){
			if(!vis[i]){
				vis[i]=1;
				for(int j=i+1;j<=(1<<n);j++){
					if(!vis[j]&&!is[i][j]){
						vis[j]=1;
						is[i][j]=1;
						if(ok) printf(" ");
						printf("%d-%d",i,j);
						ok=1; 
						break; //为这一轮的i找一个就可以了 
					}
				}
			}
		} 
		printf("\n");
	}
return 0;
}

  

【穷举】

这些都是《课课通》上面的:

例2、火柴棒等式

【问题描述】 给出 n 根火柴棒,可以拼出多少个形如“A+B=C”的等式? 等式中的 A、B、C 是用火柴棒拼出的整数(若该数非零,则最高位不能是 0)。用火柴棒拼数字 0~9 的拼法如图 9.7-1 所示。

需要注意以下几点: (1) 加号与等号各自需要两根火柴棒。 (2) 如果 A ≠ B,则 A+B=C 与 B+A=C 视为不同的等式(A、B、C 均大于或等于 0)。 (3) n 根火柴棒必须全部用上(n≤24)。 【输入样例】 14 【输出样例】 2 【样例说明】 两个等式分别为:0+1=1 和 1+0=1。

【问题分析】 首先,预处理每个数字(0~9)需要用几根火柴棒,存储在数组 f 中。然后,穷举 a 和 b,算出它们的和 c,再判断下列约束条件是否成立:

f (a)+ f (b)+ f (c)= n-4。现在的问题是:a 和 b 的范围有多大?可以发现尽量用数字 1 拼成的数比较大,分析可知最多不会超过 1111。程序实现时,分别用三个循环语句预处理好所有两位数、三位数、四位数构成所需要的火柴棒数量。

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1e4+10;
const int INF=1e9;
const int mod=1e6;
typedef long long LL;
typedef unsigned long long ull;
int f[maxn];
int main(){
	f[0]=6;f[1]=2;f[2]=5;f[3]=5;f[4]=4;f[5]=5;f[6]=6;f[7]=3;f[8]=7;f[9]=6;
	for(int i=10;i<=99;i++) f[i]=f[i/10]+f[i%10];
	for(int i=100;i<=999;i++) f[i]=f[i/100]+f[i/10%10]+f[i%10];
	for(int i=1000;i<=9999;i++) f[i]=f[i/1000]+f[i/100%10]+f[i/10%10]+f[i%10];
	int n,tot=0;
	cin>>n;
	for(int i=0;i<=1111;i++){
		for(int j=0;j<=1111;j++){
			if(f[i]+f[j]+f[i+j]==n-4) tot++;
		}
	}
	cout<<tot<<endl;
	return 0; 
}

  

例3、比例简化

【问题描述】 在社交媒体上,经常会看到针对某一个观点同意与否的民意调查以及结果。例如,对某观点表示支持的有 1498 人,反对的有 902 人,那么其比例可以简单地记为1498∶902。 因该比例的数值太大,难以一眼看出它们的关系。若把比例记为 5∶3,虽然与真实结果有一定的误差,但依然能够较为准确地反映调查结果,同时也显得比较直观。 现给出支持人数 A 和反对人数 B,以及一个上限 L,请将 A 比 B 化简为 A′ 比 B′,要求在 A′和 B′ 均不大于 L,且 A′ 和 B′ 互质(两个整数的最大公约数为 1)的前提下,A′/B′≥ A/B 且 A′/B′-A/B 的值尽可能小。

其实就是枚举,满足几个条件就可以了

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
double a,b,l;
int gcd(int x,int y){
    if(x%y==0) return y;
    else return gcd(y,x%y);
}
int main(){
    cin>>a>>b>>l;
    //其实很简单,别想复杂了
    int suma=l,sumb=1;  //注意初值
    for(int i=1;i<=l;i++){
        for(int j=1;j<=l;j++){
            if(gcd(i,j)==1&&(i*b>=j*a)&&(i*sumb<j*suma)){
                suma=i;
                sumb=j;
            }
        }
    }
    cout<<suma<<" "<<sumb<<endl;
return 0;
}

  

例6、阿姆斯特朗数

【问题描述】 编程找出所有的三位数到七位数中的阿姆斯特朗数。阿姆斯特朗数也叫水仙花数,它的定义如下:若一个 n 位自然数的各位数字的 n 次方之和等于它本身,则称这个自然数为阿姆斯特朗数。例如,153(153=1×1×1+3×3×3 +5×5×5)是一个三位的阿姆斯特朗数,8208 则是一个四位的阿姆斯特朗数。

【问题分析】 由于阿姆斯特朗数是没有规律的,只能采用穷举法一一验证 100~9999999 内的所有数是否是阿姆斯特朗数,若是,则打印之。但是,如果对任意一个数 K,都去求它的各位的若干次方,再求和判断是否等于K,效率比较差。注意到,每个位只可能是0~9,而且只会算到3~7次方。所以,为了使得程序尽快运行出正确结果,采用“以空间换时间”的策略,使用一个数组 p 预处理出所有数字的各次幂之值,p[i,j]表示 i 的 j 次方。另外,为了避免每次都对一个数进行逐位分解操作,直接用数组 a[8]存储一个数的每一位,穷举 100~9999999。 

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1e4+10;
const int INF=1e9;
const int mod=1e6;
typedef long long LL;
typedef unsigned long long ull;
int a[10],f[10][8];
int main(){
	for(int i=0;i<=8;i++) a[i]=0;
	for(int i=0;i<=9;i++) f[i][1]=i;
	for(int i=2;i<=7;i++){
		for(int j=0;j<=9;j++){
			f[j][i]=f[j][i-1]*j;
		}
	}
	int len=3,tot=0;
	a[3]=1;
	//用数组存储每一位的值,注意进位的操作 
	while(a[8]==0){
		int ans=0;
		for(int i=1;i<=len;i++) ans+=f[a[i]][len];
		int num=0;
		for(int i=len;i>=1;i--) num=num*10+a[i];
		if(ans==num) {
			tot++;
			cout<<tot<<": "<<num<<endl;
		}
		//这下面就是进位的操作 
		int i=1;
		while(a[i]==9) i++;
		a[i]++;
		for(int j=1;j<=i-1;j++) a[j]=0;
		if(i==len+1) len++;
	}
	return 0; 
}

  

对于二进制数 00000,00001,00010,…,11111,它们是严格递增有序的,如何生成类似的二进制数字序列,就是“二进制穷举”思想,其应用非常广泛。

例8、0-1 背包问题

例9、组合数的生成

【问题描述】 从 1、2、3、4、5、6 这 6 个数字中任取 4 个数的组合有1 2 3 4、1 2 3 5、1 2 3 6、1 2 4 5、1 2 4 6、1 2 5 6、1 3 4 5、1 3 4 6、1 3 5 6、1 4 5 6、2 3 4 5、2 3 4 6、2 3 5 6、2 4 5 6、3 4 5 6,共 15 种。若把它们看成 4 位数,发现是递增的。 编程,输入 n 和 r,1≤r≤n≤20,按照以上顺序,输出从 n 个数字(1~n)中任取 r 个数的所有组合。 【输入样例】 3 2 【输出样例】 1 2 1 3 2 3

观察第一个数到最后一个数的变化规律,可以看出最后一位先变化(加 1),变到 n 就要“进位”到上一位;对于倒数第二位,变化到 n-1 就要产生“进位”……每次“进位”后,本位及以后的所有位都要重新置成一个递增的序列。这个算法的思路就是二进制穷举思想,不过不是固定的“n 进制”

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1e4+10;
const int INF=1e9;
const int mod=1e6;
typedef long long LL;
typedef unsigned long long ull;
int b[1001];
//这个代码的写法很巧妙
int n,r; 
int main(){
	cin>>n>>r;
	for(int i=1;i<=n;i++) b[i]=i;
	b[0]=-1;
	while(b[0]==-1){
		for(int i=1;i<=r;i++) cout<<b[i]<<" ";
		cout<<endl;
		int j=r;
		while(b[j]==n-r+j) j--; //看后面的几位数是不是到底(最大了)
		b[j]++;
		for(int i=j+1;i<=r;i++) b[i]=b[i-1]+1; 
	}
	return 0; 
}

  

1226:装箱问题

一本通上面贪心的题目:

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
using namespace std;
int total,a[7];
int input(){
	total=0;
	for(int i=1;i<=6;i++){
		cin>>a[i];
		total+=a[i];
	}
	return total;
}
int sum=0;
void js(){

	while(input()!=0){
		sum=0;
		sum+=a[6];
		for(int i=5;i>=1;i--){
			if(i==5){
				sum+=a[5];
				if(a[1]>a[5]*11) a[1]-=a[5]*11;//装了一个5*5的箱子还能装11个1*1的箱子 
				else a[1]=0;
			}
			if(i==4){
				sum+=a[4];
				if(a[2]>=a[4]*5) a[2]-=a[4]*5;
				else {
					if(a[1]>=(a[4]*5-a[2])*4) a[1]-=(a[4]*5-a[2])*4;
					else a[1]=0;
					a[2]=0;
				}
			} 
			if(i==3){
				sum+=(a[3])/4; //每个箱子可以装4个3*3个箱子
				a[3]%=4;//要根据这个来计算
				if(a[3]==1){
					sum++; //还需要一个
					if(a[2]>=5) {
						a[2]-=5;
						if(a[1]>=7) a[1]-=7;
						else a[1]=0;
					}
					else {//2*2的箱子不够 
						if(a[1]>=27-a[2]*4) a[1]-=27-a[2]*4;
						else a[1]=0;
						a[2]=0;//不够——变为0 
					} 
				}
				if(a[3]==2){
					sum++;
					if(a[2]>=3){
						a[2]-=3;
						if(a[1]>=6) a[1]-=6;
						else a[1]=0;
					}
					else{ //a[2]不够 
						if(a[1]>=18-a[2]*4){
							a[1]-=18-a[2]*4;
						}
						else a[1]=0;
						a[2]=0;
					}
				}
				if(a[3]==3){
					sum++;
					if(a[2]>=1) {
						a[2]-=1;
						if(a[1]>=5) a[1]-=5;
						else a[1]=0;
					}
					else {
						if(a[1]>=9) a[1]-=9;
						else a[1]=0;
						a[2]=0;
					}
				} 
			}
			if(i==2){      //不要忘了i==2和i==1啊!!! 
				sum+=a[2]/9;
				a[2]%=9;
				if(a[2]>0) {
					sum++;//还可以用1*1来补
					if(a[1]>=36-a[2]*4) a[1]-=36-a[2]*4;
					else a[1]=0; //不够 
					a[2]=0; 
				}
				
			}
			if(i==1){
				sum+=a[1]/36;
				a[1]%=36;
				if(a[1]>0) sum++;
			}
		}
		cout<<sum<<endl;
	}
	
}

int main(){
	js(); 
return 0;
}

  

 posted on 2022-04-20 10:27  shirlybabyyy  阅读(80)  评论(0编辑  收藏  举报