版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
- 大致规律:可以通过发现某些规律或共性来省掉某些状态以节省时空
- 注意!! 当涉及到状态覆盖必须谨慎压维!!比如休息时间
- 例一:LCIS
- 题意:求最长公共上升子序列,n<3000
- 原数组:f[i][j]:表示 的以bi结尾的最长公共上升子序列,需要i:1…n扫A串,j:1…m扫B串,k:m…1扫B串j前的最大f[i-1][k]。优化1:f[i-1][k]更新f[i][j]时只有k在变且找最大,则可以用一个数记录最大而不用扫,减掉一个循环k。优化2:发现每次只用到到f[i-1],所以可以滚动数组或重复利用,二维减到一维,节省空间
#include<bits/stdc++.h>
using namespace std;
int n,a[3005],b[3005],f[3005];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
for(int val,i=1;i<=n;i++)
{
val=0;
for(int j=1;j<=n;j++)
{
if(b[j]<a[i]&&f[j]>val)val=f[j];
if(a[i]==b[j])
f[j]=val+1;
}
}
int ans=0;
for(int i=1;i<=n;i++)
if(f[i]>ans)
ans=f[i];
printf("%d",ans);
}
- 例二:快递服务
- 题意:给定一个完全图,三个人分别在点1.2.3,现有一些需依次到达的点,需派1人到达,问最小距离和。(位置数200,需求点1000)
- 原数组:f[i][j][k][ii]表示第一人在i,第二人在j,第三人在k,解决了ii个需求的最小距离。但明显会时空双爆。优化一:发现一个共性,当解决了第ii个需求,那一定会有一个人在ii需求的那个点上,即可减少一个表位置的状态。优化二:每次只会用到上一次需求点的状态,可以滚动数组,减少一维空间。
本题坑点:不能用floyd预处理两点最短路
#include<bits/stdc++.h>
using namespace std;
int l,n,f[205][205],q[3],dp[3][205][205];
int main()
{
scanf("%d%d",&l,&n);
for(int i=1;i<=l;i++)
{
for(int j=1;j<=l;j++)
scanf("%d",&f[i][j]);
}
memset(dp[1],0x3f,sizeof(dp[1]));
q[1]=1;
dp[1][2][3]=0;
int now=0;
while(n--)
{
scanf("%d",&q[now]);
memset(dp[now],0x3f,sizeof(dp[now]));
for(int i=1;i<=l;i++)
{
for(int j=1;j<=l;j++)
{
if(i==j||j==q[now^1]||i==q[now^1])continue;
if(dp[now^1][i][j]+f[q[now^1]][q[now]]<dp[now][i][j])
dp[now][i][j]=dp[now^1][i][j]+f[q[now^1]][q[now]];
if(dp[now^1][i][j]+f[i][q[now]]<dp[now][q[now^1]][j])
dp[now][q[now^1]][j]=dp[now^1][i][j]+f[i][q[now]];
if(dp[now^1][i][j]+f[j][q[now]]<dp[now][i][q[now^1]])
dp[now][i][q[now^1]]=dp[now^1][i][j]+f[j][q[now]];
}
}
if(n!=0)
now^=1;
}
int ans=1234567890;
for(int i=1;i<=l;i++)
{
for(int j=1;j<=l;j++)
{
if(i==j||j==q[now]||q[now]==i)continue;
if(dp[now][i][j]<ans)
ans=dp[now][i][j];
}
}
printf("%d",ans);
}