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

题目描述

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
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<" ";
    return 0;
}

[USACO06FEB]数字三角形Backward Digit Su…_第1张图片

你可能感兴趣的:(USACO,数论)