数据结构优化DP
倍增压缩DP
开车旅行(TLE13/20)
小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 HiHi。
城市 i 和城市 j 之间的距离 d[i,j] 恰好是这两个城市海拔高度之差的绝对值,即 d[i,j]=|Hi−Hj|d[i,j]=|Hi−Hj|。
旅行过程中,小A和小B轮流开车,第一天小A开车,之后每天轮换一次。每个人每天均会从一个城市出发走到另一个城市。
他们计划选择一个城市S作为起点,一直向东行驶,并且最多行驶X公里就结束旅行。
小A和小B的驾驶风格不同,小B总是沿着前进方向选择一个最近的城市作为目的地,而小A总是沿着前进方向选择第二近的城市作为目的地(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。
如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出X公里,他们就会结束旅行。
在启程之前,小A想知道两个问题:
1、对于一个给定的 X=X0X=X0,从哪一个城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值最小(如果小B的行驶路程为0,此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值都最小,则输出海拔最高的那个城市。
2、对任意给定的 X=XiX=Xi 和出发城市 SiSi,求出小A开车行驶的路程总数以及小B行驶的路程总数。
输入格式
第一行包含一个整数 N,表示城市的数目。
第二行有 N 个整数,每两个整数之间用一个空格隔开,依次表示城市 1 到城市 N 的海拔高度,即 H1,H2,…,HnH1,H2,…,Hn,且每个 HiHi 都是不同的。
第三行包含一个整数 X0X0。
第四行为一个整数 M,表示给定 M 组 SiSi 和 XiXi。
接下来的 M 行,每行包含 2 个整数 SiSi 和 XiXi,表示从城市 SiSi 出发,最多行驶 XiXi 公里。
输出格式
输出共 M+1 行。
第一行包含一个整数 S0S0,表示对于给定的 X0X0,从编号为 S0S0 的城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值最小。
接下来的 M 行,每行包含 2 个整数,之间用一个空格隔开,依次表示在给定的 SiSi 和 XiXi 下小 A 行驶的里程总数和小 B 行驶的里程总数。
数据范围
1≤N≤105
1≤M≤104
−109≤Hi≤109
0≤X0≤109
1≤Si≤N
0≤Xi≤109
数据保证Hi互不相同。
输入样例:
10
4 5 6 1 2 3 7 8 9 10
7
10
1 7
2 7
3 7
4 7
5 7
6 7
7 7
8 7
9 7
10 7
输出样例:
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0
先预处理出AB处于u时的v
倍增DP出\(f[i][x][k]\)表示从k先开车从j出发第2i天到的城市
倍增DP出\(da[i][x][k]\)\(db[i][x][k]\)表示从a或b先开车从j出发第2i天a或b经过的距离
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5,INF=1e9+5;
int n,m,S,LGN,V[N][2],id[N],f[20][N];
int LA,LB,H[N],DIS[N][2],DA[20][N],DB[20][N];
ll X;
inline bool cmp(int a,int b){return H[a]<H[b];}
void work(int j)
{
S--;
int L=abs(H[id[j]]-H[X]);
if(L<DIS[X][1]||(L==DIS[X][1]&&H[id[j]]<H[V[X][1]])) DIS[X][0]=DIS[X][1],DIS[X][1]=L,V[X][0]=V[X][1],V[X][1]=id[j];
else if(L<DIS[X][0]||(L==DIS[X][0]&&H[id[j]]<H[V[X][0]])) DIS[X][0]=L,V[X][0]=id[j];
}
void init()
{
sort(id+1,id+n+1,cmp);
for(int i=1,j;i<=n;++i)
{
X=id[i];
DIS[X][0]=DIS[X][1]=INF;
S=2;j=i;while(--j&&S) if(id[j]>X) work(j);
S=2;j=i;while(++j<=n&&S) if(id[j]>X) work(j);
}
}
void DP()
{
for(int i=1;i<=n;++i) f[0][i]=V[i][0],DA[0][i]=DIS[i][0];
for(int i=1;i<=n;++i)
f[1][i]=V[f[0][i]][1],
DA[1][i]=DIS[i][0],
DB[1][i]=DIS[f[0][i]][1];
for(int i=2;i<=LGN;++i)
for(int j=1;j<=n-(1<<i);++j)
f[i][j]=f[i-1][f[i-1][j]],
DA[i][j]=DA[i-1][j]+DA[i-1][f[i-1][j]],
DB[i][j]=DB[i-1][j]+DB[i-1][f[i-1][j]];
}
void calc(int S,ll X)
{
LA=LB=0;
for(int i=LGN;i>=0;--i)
if(f[i][S]&&(ll)(LA+LB+DA[i][S]+DB[i][S])<=X)
{
LA+=DA[i][S];
LB+=DB[i][S];
S=f[i][S];
}
}
int main()
{
scanf("%d",&n);
LGN=log2(n);
for(int i=1;i<=n;++i) scanf("%lld",&H[i]),id[i]=i;
init();
DP();
scanf("%lld",&X);
double B=INF/1.0,B_;
for(int i=n;i;--i)//预先H从小到大排序过,倒序就是高度从大到小
{
int I=id[i];
calc(I,X);B_=1.0*LA/LB;
if(LB&&B_<B) B=B_,S=I;
}
printf("%d\n",S);
scanf("%d",&m);
while(m--)
{
scanf("%d %lld",&S,&X);
calc(S,X);
printf("%d %d\n",LA,LB);
}
}
计算重复
定义 conn(s,n) 为 n 个字符串 s 首尾相接形成的字符串,例如conn(“abc”,2)=”abcabc”称字符串 a 能由字符串 b 生成,当且仅当从字符串 b 中删除某些字符后可以得到字符串 a。例如“abdbec”可以生成“abc”,但是“acbbe”不能生成“abc”。
给定两个字符串 s1 和 s2,以及两个整数 n1 和 n2,求一个最大的整数 m,满足conn(conn(s2,n2),m) 能由 conn(s1,n1) 生成。
输入格式
输入包含多组测试数据。
每组数据由2行组成,第一行包含
s2,n2,第二行包含s1,n1。
输出格式
对于每组数据输出一行表示答案m。
数据范围
s1 和 s2 长度不超过100,n1 和
n2 不大于 106。
输入样例:
ab 2
acb 4
acb 1
acb 1
aa 1
aaa 3
baab 1
baba 11
aaaaa 1
aaa 20
输出样例:
2
1
4
7
12
#include<bits/stdc++.h>
using namespace std;
#define N 105
#define M 26
string s1,s2;
long long n1,n2,f[N][M+5];
//s1 product s2
/*
the log(s1*n1/s2) of composable s2's
maximum length is between 26 and 27
*/
/*
f:
Starting from position I, S1 constitutes
the minimum number of characters required for S2
*/
void dp()
{
//enumerate starting point in s1
for(int i=0;i<s1.size();++i)
{
int pos=i;//indicator
f[i][0]=0;
//enumerate s2
for(int j=0;j<s2.size();++j)
{
int cnt=0;
//find s2[j] in order in s1
while(s1[pos]!=s2[j])
{
pos=(pos+1)%s1.size();
if(++cnt>=s1.size())
{
printf("0\n");
return;
}
}
//s2[j] is equal to s1[pos],so s2[j+1] is equal to s1[>=pos+1]
pos=(pos+1)%s1.size();
f[i][0]+=cnt+1;
}
}
for(int j=1;j<=30;++j)
for(int i=0;i<s1.size();++i)
f[i][j]=f[i][j-1]+f[(i+f[i][j-1])%s1.size()][j-1];
long long m=0;
for(int st=0;st<s1.size();++st)
{
long long x=st,ans=0;
for(int k=30;k>=0;--k)
{
//if it's in legal scope
if(x+f[x%s1.size()][k]<=s1.size()*n1)
{
x+=f[x%s1.size()][k];
ans+=1<<k;
}
}
m=max(m,ans);
}
printf("%lld\n",m/n2);
}
int main()
{
while(cin>>s2>>n2>>s1>>n1)
{
dp();
}
}

浙公网安备 33010602011771号