有这么一个游戏:
写出一个11至NN的排列a_ia
i
,然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少11,直到只剩下一个数字位置。下面是一个例子:
3,1,2,43,1,2,4
4,3,64,3,6
7,97,9
1616
最后得到1616这样一个数字。
现在想要倒着玩这样一个游戏,如果知道NN,知道最后得到的数字的大小sumsum,请你求出最初序列a_ia
i
,为11至NN的一个排列。若答案有多种可能,则输出字典序最小的那一个。
[color=red]管理员注:本题描述有误,这里字典序指的是1,2,3,4,5,6,7,8,9,10,11,121,2,3,4,5,6,7,8,9,10,11,12
而不是1,10,11,12,2,3,4,5,6,7,8,91,10,11,12,2,3,4,5,6,7,8,9[/color]
输入格式
两个正整数n,sumn,sum。
输出格式
输出包括11行,为字典序最小的那个答案。
当无解的时候,请什么也不输出。(好奇葩啊)
输入输出样例
输入 #1
4 16
输出 #1
3 1 2 4
说明/提示
对于40%40%的数据,n≤7n≤7;
对于80%80%的数据,n≤10n≤10;
对于100%100%的数据,n≤12,sum≤12345n≤12,sum≤12345。
解题思路:
很新颖的搜索方式,将搜索和杨辉三角结合在了一起,首先你可以看出他的规律,就是他每个数所在的位置决定了它为最终加和得到的那个数做了多少贡献。比如说样例这个数,每个数的贡献就是1,3,3,1,也就是第一个数加了一次,第二个数加了三次,以此类推。所以我们先讲每一个n所对应的杨辉三角的数写出来,然后开始搜索。首先我们搜索的初始时(0,-1,0),然后判断两个情况的时候重新搜索,也就是所谓的剪枝。再然后就是搜索的细节了,每个位置从1到n,如果这个数没有用过的话就开始使用这个数搜索,这样的话可以保证答案的字典序最小,恩,差不多要注意的就是这么多。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n , sum;
int vis[N];
int ans[N];
int flag;
const int PT[][13] = //先打一张杨辉三角表
{
{ 1 } , // N = 1
{ 1 , 1 } , // N = 2
{ 1 , 2 , 1 } , // N = 3
{ 1 , 3 , 3 , 1 } , // N = 4
{ 1 , 4 , 6 , 4 , 1 } , // N = 5
{ 1 , 5 , 10, 10, 5 , 1 } , // N = 6
{ 1 , 6 , 15, 20, 15, 6 , 1 } , // N = 7
{ 1 , 7 , 21, 35, 35, 21, 7 , 1 } , // N = 8
{ 1 , 8 , 28, 56, 70, 56, 28, 8 , 1 } , // N = 9
{ 1 , 9 , 36, 84,126,126, 84, 36, 9 , 1 } , // N = 10
{ 1 , 10, 45,120,210,252,210,120, 45, 10 , 1 } , // N = 11
{ 1 , 11, 55,165,330,462,462,330,165, 55 ,11 , 1 }
};
int dfs(int p , int k , int res)//p是第p个数,k是值,res是当前的总和
{
if(res > sum)
return 0;
if(p==n)
{
if(res == sum)
{
ans[p] = k;
return 1;
}
else
return 0;
}
vis[k] = 1;
for(int i = 1 ; i <= n ; i++)
{
if(vis[i]==0&&dfs(p+1,i,res+PT[n-1][p]*i))
{
ans[p] = k;
return 1;
}
}
vis[k] = 0;
return 0;
}
int main()
{
cin >> n >> sum;
if(dfs(0,-1,0))
{
for(int i = 1 ; i<= n ; i++)
{
cout << ans[i] << ' ';
}
cout << endl;
}
return 0;
}