一道题加深对遍历的理解(多重for循环如何写)

一道题加深对遍历的理解(多重for循环如何写)

题目:

`期末考试有三种题型,选择题,每题2.3分,填空题,每题3.4分,编程题,每题12分。

张小元一共做出了n道题,最终的得分是m,问张小元做出了选择题,填空题和编程题各几道。如无解,输出Error!
输入:做出的总题数n,最终得分m
输出:依次输出编程题,填空题和选择题的数目,如果有多组结果满足条件,则依次按照编程题的数目,填空题的数目和选择题的数目降序输出。`

分析:

这是一道较为简单的遍历题,但如何写得效率一点,好看一点呢?以前没细想郭这个问题,现在来分析一下;

  • 假如我们喜欢暴力枚举,而且枚举时也不喜欢考虑任何问题,那么我们甚至可以写成这样:

    void demo2() {
    	int n, m;
    	scanf("%d%d", &n, &m);
    	char flag = 0;
    	for (int i = n; i >= 0; i--) {
    		for (int j = 0; j <= n; j++) {
    			for (int k = 0; k <= n; k++) {
    				if (i + j + k == n) {
    					if (fabs(i * 2.3 + j * 3.4 + k * 12 - m) < 1e-4) {
    						printf("%d %d %d\n", k, j, i);
    						flag++;
    					}
    				}
    			}
    		}
    	}
    	if (!flag) {
    		printf("Error!\n");
    	}
    }
    int main() {
    	demo2();
    }
    

    这个用例就是相当于把一个n*n*n空间的所有点先找出来,然后再去对他做约束,但其实在数学中我们知道如果知道约束了,我们可以忽略约束之外的那部分点,直接在约束之内找:

    \[这样我们的执行次数就相当于\\ \int_{n}^{0}di\int_{0}^{n}dj\int_{0}^{n}dk \]

    很遗憾,出题者不允许这种浪费,于是专门设了过不了的一个用例:image-20220614203208980

  • 假如我们知道了可以利用多重积分的概念来约束上下限,那么这样我们的程序就会变得高效、美观很多;

    #include<stdio.h>
    #include<math.h>
    void demo1(){
    	int n,m;
    	scanf("%d%d",&n,&m);
    	char flag=0;
    	for(int i=n;i>=0;i--){
    		for(int j=0;i+j<=n;j++){
    			int k=n-i-j;
    			if(fabs(i*2.3+j*3.4+k*12-m)<1e-4){
    				printf("%d %d %d\n",k,j,i);
    				flag++;
    			}
    		}
    	}
    	if(!flag){
    		printf("Error!\n");
    	}
    }
    int main(){
    	demo1();
    }
    

    \[这样我们的执行次数就相当于\\ \int_{n}^{0}di\int_{0}^{n-i} 1\cdot dj \]

  • 在上面我们也发现了很美观的事实,通常我们都是顺序遍历,但恰好这道题要求的是逆序输出,很简单的,我们将i的遍历从0n改为从n0,而对j的上下限约束,我们好像就是通过数学中的二重积分一样给它划好了上下限(但其实更有利的地方程序中可以直接把上下限写成一个判断的语句,这样就更加有利了我们的积分操作了),这样的话我们就节省了很多不必要的运行次数。(就过了)image-20220614203246935

posted @ 2022-06-27 00:03  Link_kingdom  阅读(52)  评论(0编辑  收藏  举报