您的位置:首页 > Web前端

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

2017-08-23 10:38 351 查看
题目描述

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
[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;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: