「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
题目如下:
刷蓝桥杯的时候看到这题,数据规模感觉不大,可以尝试使用暴力,找出所有的情况,但是又无从下手,不知道怎么枚举找出所有的情况。搜遍全网也找不到这道题的答案,于是还是自己想了半天还是勉强做出来了。
思路
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全排列。
假设分成三组,那就取前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;
这道题目数据规模不大,不会出现超时问题。至于能适用多大的规模还不太清楚。有类似问题用这个方法应该不会有问题。