https://oj.jdfz.com.cn/oldoj/problem.php?id=1821
题解:本题求最长下降子序列,然后求最长下降子序列的个数,并且不能重复。这就尴尬了……
才发现,我竟然只会前一半……
最近疯狂刷DP,还是有一些成效的,至少可以写出一个靠谱点的动态转移方程了,
设g[i]代表当到第i个数时,长度最长的最长下降子序列个数。
得出 g[i]=sum(g[j]) (f[j]+1=f[i])
若g[i]==0就g[i]=1
于是: WA 82%
#include<stdio.h>
int n;
int num[5003];
int f[5003];
int g[5003];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
}
f[1]=1;
g[1]=1;
for(int i=2;i<=n;i++)
{
f[i]=1;
g[i]=1;
for(int j=1;j<i;j++)
{
if(num[j]>num[i])
{
if(f[j]+1>f[i])
{
f[i]=f[j]+1;
g[i]=g[j];
}
else if(f[j]+1==f[i])
{
g[i]+=g[j];
}
}
}
}
int max=0;
int sum=0;
for(int i=1;i<=n;i++)
{
if(max<f[i])
{
max=f[i];
sum=g[i];
}
else if(max==f[i])
{
sum+=g[i];
}
}
printf("%d %d\n",max,sum);
return 0;
}
看了看题,发现有一句话没有注意:“在计算解的数量的时候,如果两个解所组成的字符串相同,那么这样的两个解被认为是相同的(只能算做一个解)。因此,两个不同的购买方案可能产生同一个字符串,这样只能计算一次。”
于是,我冥思苦想,终于还是没想出来,就看了题解:
如果有两个方案中出现序列一样,视为一个方案,要需要加一个数组next 用next[i] 记录和第i个数情况一样(即:f[i]=f[j] 且 num[i]=num[j])可看做一个方案的最近的位置。
为了方便操作可以将num[n+1]赋值为-1这样可以认为第n+1 个一定可以买,答案就是f[n+1] g[n+1]
于是:写了这个,WA 27%
#include<stdio.h>
#include<string.h>
int n;
int num[5003];
int f[5003];
int g[5003];
int next[5003];
int v[5003];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
}
n++;
num[n]=-1;
f[1]=1;
for(int i=2;i<=n;i++)
{
f[i]=1;
for(int j=1;j<i;j++)
{
if(num[j]>num[i])
{
if(f[j]+1>f[i])
{
f[i]=f[j]+1;
}
}
}
}
for(int i=1;i<=n;i++)
{
next[i]=n+1;
for(int j=i+1;j<=n;j++)
{
if(num[i]==num[j]&&f[i]==f[j])
{
next[i]=j;
break;
}
}
}
g[1]=1;
for(int i=2;i<=n;i++)
{
for(int j=1;j<i;j++) v[j]=0;
//g[i]=1;
for(int j=1;j<i;j++)
{
if(v[j]) continue;
int x=j;
while(next[x]<i)
{
v[x]=1;
x=next[x];
}
v[x]=1;
if(num[x]>num[i]&&f[x]+1==f[i]) g[i]+=g[x];
}
if(!g[i]) g[i]=1;
}
printf("%d %d\n",f[n]-1,g[n]);
return 0;
}
改用long long WA 9%
看题解发现需要高精度,不想写了
#include<stdio.h>
long long n;
long long num[5003];
long long f[5003];
long long g[5003];
long long next[5003];
long long v[5003];
int main()
{
scanf("%lld",&n);
for(long long i=1;i<=n;i++)
{
scanf("%lld",&num[i]);
}
n++;
num[n]=-1;
f[1]=1;
for(long long i=2;i<=n;i++)
{
f[i]=1;
for(long long j=1;j<i;j++)
{
if(num[j]>num[i])
{
if(f[j]+1>f[i])
{
f[i]=f[j]+1;
}
}
}
}
for(long long i=1;i<=n;i++)
{
next[i]=n+1;
for(long long j=i+1;j<=n;j++)
{
if(num[i]==num[j]&&f[i]==f[j])
{
next[i]=j;
break;
}
}
}
g[1]=1;
for(long long i=2;i<=n;i++)
{
for(long long j=1;j<i;j++) v[j]=0;
//g[i]=1;
for(long long j=1;j<i;j++)
{
if(v[j]) continue;
long long x=j;
while(next[x]<i)
{
v[x]=1;
x=next[x];
}
v[x]=1;
if(num[x]>num[i]&&f[x]+1==f[i]) g[i]+=g[x];
}
if(!g[i]) g[i]=1;
}
printf("%lld %lld\n",f[n]-1,g[n]);
return 0;
}
/*
6
5 6 4 3 1 2
*/