题目链接: POJ—3187
每个数加的次数刚好就是杨辉三角,然后暴力就可以了。
这里我用了一个剪枝(虽然跟大牛的比不了,但还是快了不少79ms成了16ms):就是当已经计算出一半的时候,此时剩下部分的杨辉三角是降序排列的,这时将没有用过的数字升序排列得到的sum应该是当前情况下最小的sum,但如果仍然比答案大就可以减掉了。
举个例子:
input
4 16
output
3 1 2 4
在搜索过程中,先试1,剩下三个位置在杨辉三角(1,3,3,1)里就已经是降序排列了,将4,3,2放入剩下的三个位置,发现:1*3+4*3+3*3+2*1 = 26 大于 16,所以剪掉,可以直接试2了。
/************************************
Problem: 3187 User: ChenyangDu
Memory: 696K Time: 16MS
Language: G++ Result: Accepted
*************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,sum,mod[12],yang[20];//yang杨辉三角;mod是生成的排列
bool usd[12];
void cal_yang(){ //计算杨辉三角
for(int i=1;i<=n;i++){
for(int j=i;j>=1;j--){
yang[j] = yang[j] + yang[j-1];
}
}
}
int cal_s(){ //计算此时mod数组对应的sum
int s = 0;
for(int i=1;i<=n;i++){
s += mod[i]*yang[i];
}
return s;
}
bool dfs(int p){
if(p>n && cal_s() == sum){ //找到答案了
for(int i=1;i<=n;i++)
cout<<mod[i]<<" ";
return true;
}
if(p>(n-1)>>1){ //剪枝
int j = 1;
for(int i=p;i<=n;i++){
for(;j<=n;j++){
if(usd[j] == false){
mod[i] = j;
break;
}
}
}
if(cal_s()>sum){
return false;
}
}
for(int i=1;i<=n;i++){
if(usd[i] == false){
usd[i] = true;
mod[p] = i;
if(dfs(p+1))return true;
usd[i] = false; //回溯
}
}
return false;
}
int main(){
cin>>n>>sum;
yang[1] = 1;
cal_yang();
dfs(1);
return 0;
}