题目大意
求一个字典序最小的n项数列a,要求每一项都是非负整数。
需要满足两个要求:
1、和为s
2、两两差的绝对值和为k
搜索
首先最优情况下a一定非降。
我们把序列差分,bi=ai-ai-1。
那么
∑ni=1ai=∑ni=1∑ij=1bj=∑ni=1bi∗(n−i+1)
∑ni=1∑nj=iaj−ai=∑ni=1∑nj=i∑jk=i+1bk=∑ni=1bi∗(i−1)∗(n−i+1)
我们现在按字典序搜索b,用(x,y,z)表示一个状态目前填到位置x,和还剩下y差和还剩下z。显然可以加个简单的可行性减枝就是当前位置填某个值或更大都会使得y<0或z<0的状态就直接退出。
这样还是很慢。我们每次搜到合法就直接退出,因为此时字典序最小。那么我们可以记忆化表示一个状态(x,y,z)是否访问过,访问过的一定搜不到解,可以直接退出。
由于状态和n有关,因此每组数据需要情况记忆化数组。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=50+10,maxs=200+10,maxk=2000+10;
bool f[maxn][maxs][maxk];
int a[maxn],b[maxn],sta[maxn*maxs*maxk][3];
int i,j,s,k,l,t,n,m,top,ca;
bool czy;
void dfs(int x,int y,int z){
if (x==n+1){
if (y==0&&z==0) czy=1;
return;
}
int i,j=0;
if (f[x][y][z]) return;
fo(i,0,s){
if (z-i*(x-1)*(n-x+1)<0||y-i*(n-x+1)<0) break;
b[x]=i;
dfs(x+1,y-i*(n-x+1),z-i*(x-1)*(n-x+1));
if (czy) return;
}
f[x][y][z]=1;
sta[++top][0]=x;
sta[top][1]=y;
sta[top][2]=z;
}
int main(){
freopen("data.in","r",stdin);freopen("data.out","w",stdout);
scanf("%d",&ca);
while (ca--){
scanf("%d%d%d",&n,&s,&k);
czy=top=0;
dfs(1,s,k);
if (czy){
fo(i,1,n) a[i]=a[i-1]+b[i];
fo(i,1,n) printf("%d ",a[i]);
}
else printf("-1");
printf("\n");
while (top){
j=sta[top][0];k=sta[top][1];t=sta[top][2];
f[j][k][t]=0;
top--;
}
}
}