[USACO06FEB]数字三角形Backward Digit Su…

来源:互联网 发布:知乎周刊在哪里看 编辑:程序博客网 时间:2024/06/07 00:02

题目描述

3   1   2   4  4   3   6    7   9     16

有这么一个游戏:
写出一个1~N的排列a[i],然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少1,直到只剩下一个数字位置。
现在想要倒着玩这样一个游戏,如果知道N,知道最后得到的数字的大小sum,请你求出最初序列a[i],为1~N的一个排列。若答案有多种可能,则输出字典序最小的那一个。当无解的时候,请什么也不输出。(好奇葩啊)
数据规模
对于100%的数据,n≤12,sum≤12345
思路
注意到这样一个事实:
N=4时,正序执行的话最后答案应该是a[1]+3*a[2]+3*a[3]+a[4]
N=5时,正序执行的话最后答案应该是a[1]+4*a[2]+6*a[3]+4*a[4]+a[5]
所以不难发现这样一个规律
ANS=∑(a[i]*C(n,i))
这样之后这个题就比较简单了,我们发现数据范围很小,所以DFS加点剪枝应该就没问题了。每次枚举可用的点,然后有一个可行性剪枝:当个数>N时检查并退出循环,还有一个就是当当前结果>sum时直接跳出本轮循环,返回到上一个数。
当然我们还有一种不是很完美的方法,就是用C++STL的next_permutation,枚举全排列并检查,USACO上过7个点,3个TLE,但是本地评测能过,耗时最长点为0.006s,这就比较尴尬了。
接下来上代码吧:
代码:
(我做的原题是考试题(并不是NOIP DAY2 T3的)愤怒的小鸟)

#include<iostream>using namespace std;int i,j,m,n;int a[1001][1001],ans[1001];int b[1001],kk,s;int r(){    int ans=0,f=1;    char ch;    while(ch<'0'||ch>'9')    {        if(ch=='-')        f=-1;        ch=getchar();    }    while(ch>='0'&&ch<='9')    {        ans*=10;        ans+=ch-'0';        ch=getchar();    }    return ans*f;}void triangle(){    for(i=1;i<=n;i++)    {        a[i][1]=a[i][i]=1;    }    for(i=2;i<=n;i++)    for(j=2;j<=i;j++)    {        a[i][j]=a[i-1][j]+a[i-1][j-1];    }}void dfs(int x,int sum){    if(x==n+1&&sum==s) {kk=1;return;}    else if(x==n+1&&sum!=s) return;    if(sum>s) return;    for(int i=1;i<=n;i++)    {        if(!b[i])        {            b[i]=1,ans[x]=i;            dfs(x+1,sum+i*a[n][x]);            if(kk)            return;            b[i]=0;            ans[x]=0;        }    }}int main(){    n=r(),s=r();    triangle();    dfs(1,0);    if(!ans[1])    return 0;    for(i=1;i<=n;i++)    cout<<ans[i]<<" ";    return 0;}

这里写图片描述

原创粉丝点击