数据结构优化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();
	}
}
posted @ 2020-10-23 19:32  林生。  阅读(115)  评论(0)    收藏  举报