一、问题描述
话说大诗人李白,一生好饮。幸好他从不开车。一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:无事街上走,提壶去打酒。逢店加一倍,遇花喝一斗。这一路上,他一共遇到店5次,遇到花10次,已知最后遇到的是花,他正好把酒喝光了。
请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能的方案的个数(包含题目给出的)。
注意:通过浏览器提交答案。答案是个整数。不要书写任何多余的内容。
二、解题思路
用1代表遇到花,0代表遇到店。最后一位固定为1,前面14位必须有9个1,5个0。
元素不变,顺序可变,不同的排列顺序对应一种可能的方案,可以将解空间看成是14个数的排列问题。但是存在重复的元素,应该采用可以处理重复元素的全排列方法。
三、相关算法
1.交换法
代码如下(示例):
#include <bits/stdc++.h>
using namespace std;
int number=0;
int a[20]={0,0,0,0,0,1,1,1,1,1,1,1,1,1,1};
bool check(){
int res=2;
for(int i=0;i<15;i++){
if(a[i]==0){
res*=2;
}else{
res-=1;
}
}
if(res==0){
return true;
}else{
return false;
}
}
void f(int p,int a[],int n){
if(p==n){
if(check()){
number++;
}
return;
}
for(int i=p;i<n;i++){
if(i>p&&a[i]==a[i-1]){
continue;
}
swap(a[i],a[p]);
f(p+1,a,n);
swap(a[i],a[p]);
}
}
int main(){
f(0,a,14);
cout<<number<<endl;
return 0;
}
2.抽取法
代码如下(示例):
#include <bits/stdc++.h>
using namespace std;
int number=0;
int a[20]={0,0,0,0,0,1,1,1,1,1,1,1,1,1,1};
int b[20];
int vis[20]={0};
bool check(){
int res=2;
for(int i=0;i<14;i++){
if(b[i]==0){
res*=2;
}else{
res-=1;
}
}
res-=1;
if(res==0){
return true;
}else{
return false;
}
}
void f(int a[],int n,int b[],int p){
if(p==n){
if(check()){
number++;
}
return;
}
for(int i=0;i<n;i++){
if(i>0&&a[i]==a[i-1]&&!vis[i-1]){
continue;
}
if(!vis[i]){
b[p]=a[i];
vis[i]=1;
f(a,n,b,p+1);
vis[i]=0;
}
}
}
int main(){
f(a,14,b,0);
cout<<number<<endl;
return 0;
}
3.使用next_permutation求解
代码如下(示例):
#include <bits/stdc++.h>
using namespace std;
int number=0;
int a[20]={0,0,0,0,0,1,1,1,1,1,1,1,1,1,1};
bool check(){
int res=2;
for(int i=0;i<15;i++){
if(a[i]==0){
res*=2;
}else{
res-=1;
}
}
if(res==0){
return true;
}else{
return false;
}
}
int main(){
do{
if(check()){
number++;
}
}while(next_permutation(a,a+14));
cout<<number<<endl;
return 0;
}
四、运算结果
答案:14