蓝桥杯——算法训练(粘木棍)

626 阅读3分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

题目如下:

image.png

刷蓝桥杯的时候看到这题,数据规模感觉不大,可以尝试使用暴力,找出所有的情况,但是又无从下手,不知道怎么枚举找出所有的情况。搜遍全网也找不到这道题的答案,于是还是自己想了半天还是勉强做出来了。

思路

N根木棍粘成M个木棍,就相当于N个木棍,分成M组,然后再找出最长和最短的那组相减算出来等于多少。

第一步

将每个木棍进行标号,从0到N-1。0到N-1个号中间就相当于有N-1个空挡。分成M组,就要在这个N-1个空挡放M-1个挡板,就能成功将这N个木棍分成M组。这里就需要用到全排列函数next_permutation()了,不了解这个函数的同学可以先去了解一下,这个函数非常强大,暴力求解全排列的题目基本都要用到它。分成M组就要在N-1个空挡中随机插M-1个挡板。这个随机插入就可以使用这个全排列公式了。这里N-1个空挡可以转换一下,一共有1到N,N个数,如果分成两组,那就取一个挡板,假设是放在1这个位子,那么1之前的就作为一组,1之后包括下标为1的这个数作为第二组,就可以分组了。这里大家可以自己细想一下。数的下标是0到N-1.然后挡板取得是1到N。对挡板进行全排列,全排列之后只去前M-1个数。这里举一个例子,1234全排列。

image.png

假设分成三组,那就取前2个数,就是1,2 1,3 1,4 2,3, 2,4, 3,4。用set来存储,不会造成重复。当然了,会造成很多时间的浪费,但是我也没想到很好的解决办法。

这部分代码如下:

#include<bits/stdc++.h>
using namespace std;
int N,M;
int a[9];
int m[9];
set< set<int> > s;
set<int> b[10000];
int max(int a,int b){
	if(a<b)
	return b;
	else
	return a;
}
int min(int a,int b)
{
	if(a>b)
	return b;
	else
	return a;
}
int main(){
	cin>>N>>M;
	for(int i=0;i<N;i++){
		cin>>a[i];
		m[i]=i+1;
	}
	int j=0;
	if(M==1)
	{
		cout<<0;
		return 0;
	}
	do  
        {  
        for(int i=0;i<M-1;i++)
		b[j].insert(m[i]);
		j++;
        }while(next_permutation(m,m+N-1));
        for(int i=0;i<j;i++){
            s.insert(b[i]);
        }
        j=0;
        for(set< set<int> >::iterator it=s.begin(); it!=s.end(); it++){
            b[j]=*it;
            j++;
	}

第二步

把所有的排列序号都存储好了之后,就可以直接遍历分组得出答案了,但是这N根木棍也需要全排列一次。举个例子,1,2,3,4,分两组,运用挡板法,1和4永远也分不到一组,必须将这个四个数也全排列一次,得到1,4,2,3这个时候用挡板法才14组才能分到一起。每次排列之后,都用一次挡板法,将结果存储,最后就能得出答案。 这部分代码如下:

        int Max=0;
	int Min=0x3f3f3f3f;
	int ans=100000;
	do
	{ 
	for(int i=0;i<j;i++){
		Max=0;
		Min=0x3f3f3f3f;
		int d[M];
		d[0]=0;
		int jj=1;
		for(set<int>::iterator it=b[i].begin(); it!=b[i].end(); it++){
			d[jj]=*it;
			jj++;
		}
		int mmm=0;
		for(int k=0;k<=d[1]-1;k++)
    			mmm+=a[k];
    	Max=max(mmm,Max);
    	Min=min(mmm,Min);
    	for(int kk=1;kk<M-1;kk++){
    		int nn=0;
    		for(int k=d[kk];k<=d[kk+1]-1;k++)
    		nn+=a[k];
    		Max=max(nn,Max);
    		Min=min(nn,Min);
    	}
    	int mm=0;
    	for(int k=d[M-1];k<=N-1;k++){
    			mm+=a[k];
    	}
    	Max=max(mm,Max);
    	Min=min(mm,Min);
		ans=min(ans,Max-Min);
	}
	}while(next_permutation(a,a+N));
	cout<<ans;

这道题目数据规模不大,不会出现超时问题。至于能适用多大的规模还不太清楚。有类似问题用这个方法应该不会有问题。