写在前面:
用倍增优化的动态规划算法求解问题时,一般分为两部分,第一部分是预处理,用阶段成倍增长的DP,计算出若干与2的整数次幂相关的代表状态。第二部分是拼凑,基于二进制拆分思想,用上一部分得到的代表状态组合成最终答案。
一、P1081 开车旅行:
题目描述
小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i的海拔高度为Hi,城市 i和城市 j之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i,j]=|Hi - Hj|
旅行过程中,小 A和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次。他们计划选择一个城市 S 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行。小 A 和小 B的驾驶风格不同,小 B总是沿着前进方向选择一个最近的城市作为目的地,而小 A总是沿着前进方向选择第二近的城市作为目的地(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出 X 公里,他们就会结束旅行。
在启程之前,小 A想知道两个问题:
对于一个给定的 X=X0,从哪一个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值最小(如果小 B 的行驶路程为 0,此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小 A 开车行驶的路程总数与小 B行驶的路程总数的比值都最小,则输出海拔最高的那个城市。
对任意给定的 X=Xi 和出发城市 Si,小 A 开车行驶的路程总数以及小 B行驶的路程总数。
输入格式
第一行包含一个整数 N,表示城市的数目。
第二行有 N个整数,每两个整数之间用一个空格隔开,依次表示城市 1 到城市 N的海拔高度,即 H1,H2,…,Hn,且每个 Hi都是不同的。
第三行包含一个整数 X0。
第四行为一个整数 M,表示给定 M组 Si和 Xi。
接下来的 M 行,每行包含 2 个整数 Si和 Xi,表示从城市 Si出发,最多行驶 Xi 公里。
输出格式
输出共 M+1 行。
第一行包含一个整数 S0 ,表示对于给定的 X0,从编号为 S0的城市出发,小 A开车行驶的路程总数与小 B 行驶的路程总数的比值最小。
接下来的 M行,每行包含 2 个整数,之间用一个空格隔开,依次表示在给定的 Si和Xi 下小 A 行驶的里程总数和小 B 行驶的里程总数。
输入输出样例
输入 #1 复制
4
2 3 1 4
3
4
1 3
2 3
3 3
4 3
输出 #1 复制
1
1 1
2 0
0 0
0 0
输入 #2 复制
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 复制
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#define ll long long
#define llu unsigned ll
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7;
const int maxn=100100;
// 0 A
// 1 B
int f[maxn][20][2],nt[maxn][2],h[maxn];
ll da[maxn][20][2],db[maxn][20][2];
ll la,lb,maxa,maxb;
int n,m,x0,xi,si,ans,t;
struct node
{
int id,hi;
node(){}
node(int a,int b)
{
id=a,hi=b;
}
friend bool operator <(const node &a,const node &b)
{
return a.hi<b.hi;
}
};
struct nn
{
int id;
int dis;
int hi;
nn(){}
nn(int a,int b,int c)
{
id=a,dis=b,hi=c;
}
friend bool operator <(const nn &a,const nn &b)
{
if(a.dis!=b.dis) return a.dis<b.dis;
else return a.hi<b.hi;
}
}_nt[10];
set<node>se;
set<node>::iterator it,lit,rit;
void get_dis(int si,int xi)
{
la=lb=0;
for(int i=t;i>=0;i--)
{
if(f[si][i][0]&&da[si][i][0]+db[si][i][0]<=xi)
{
xi-=da[si][i][0]+db[si][i][0];
la+=da[si][i][0],lb+=db[si][i][0];
si=f[si][i][0];
}
}
}
int main(void)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&h[i]);
}
node res;
for(int i=n;i>0;i--)
{
res=node(i,h[i]);
se.insert(res);
it=se.find(res);
lit=rit=it;
int cnt=0;
if(lit!=se.begin()) lit--,_nt[++cnt]=nn(lit->id,abs(h[lit->id]-h[i]),h[lit->id]);
if(lit!=se.begin()) lit--,_nt[++cnt]=nn(lit->id,abs(h[lit->id]-h[i]),h[lit->id]);
if(rit++,rit!=se.end()) _nt[++cnt]=nn(rit->id,abs(h[rit->id]-h[i]),h[rit->id]);
if(rit!=se.end()) if(rit++,rit!=se.end()) _nt[++cnt]=nn(rit->id,abs(h[rit->id]-h[i]),h[rit->id]);
sort(_nt+1,_nt+cnt+1);
if(cnt) nt[i][1]=_nt[1].id;
if(cnt>=2) nt[i][0]=_nt[2].id;
}
for(int i=1;i<=n;i++)
{
if(nt[i][0]) f[i][0][0]=nt[i][0],da[i][0][0]=abs(h[nt[i][0]]-h[i]),db[i][0][0]=0;
if(nt[i][1]) f[i][0][1]=nt[i][1],da[i][0][1]=0,db[i][0][1]=abs(h[nt[i][1]]-h[i]);
}
t=log2(n);
for(int j=1;j<=t;j++)
{
for(int i=1;i<=n;i++)
{
for(int k=0;k<=1;k++)
{
int cm=0;
if(j==1) cm=k^1;
else cm=k;
if(f[i][j-1][k]) f[i][j][k]=f[f[i][j-1][k]][j-1][cm];
if(f[i][j][k])
{
da[i][j][k]=da[i][j-1][k]+da[f[i][j-1][k]][j-1][cm];
db[i][j][k]=db[i][j-1][k]+db[f[i][j-1][k]][j-1][cm];
}
}
}
}
scanf("%d",&x0);
maxa=1,maxb=0;
ans=0;
for(int i=1;i<=n;i++)
{
get_dis(i,x0);
if(!lb) la=1;
if(maxa*lb>maxb*la||maxa*lb==maxb*la&&h[i]>h[ans]) maxa=la,maxb=lb,ans=i;
}
printf("%d\n",ans);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&si,&xi);
get_dis(si,xi);
printf("%lld %lld\n",la,lb);
}
return 0;
}
二、ACWing 294. 计算重复:
定义 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
这题数据好像有问题,scanf会多输出一个0,可能是最后多了一个单组的样例?
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#define ll long long
#define llu unsigned ll
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7;
const int maxn=100100;
char str1[108],str2[108];
int n1,n2;
ll f[108][32];
void solve()
{
int len1=strlen(str1);
int len2=strlen(str2);
for(int i=0;i<len1;i++)
{
int pos=i;
f[i][0]=0;
for(int j=0;j<len2;j++)
{
int cnt=0;
while(str1[pos]!=str2[j])
{
pos=(pos+1)%len1;
cnt++;
if(cnt>=len1)
{
printf("0\n");
return ;
}
}
pos=(pos+1)%len1;
f[i][0]+=cnt+1;
}
}
for(int j=1;j<=30;j++)
{
for(int i=0;i<len1;i++)
f[i][j]=f[i][j-1]+f[(i+f[i][j-1])%len1][j-1];
}
ll maxx=0;
for(int i=0;i<len1;i++)
{
ll res=i,ans=0;
for(int j=30;j>=0;j--)
{
if(res+f[res%len1][j]<=len1*n1)
{
res+=f[res%len1][j];
ans+=1<<j;
}
}
maxx=max(maxx,ans);
}
printf("%lld\n",maxx/n2);
}
int main(void)
{
while(cin>>str2>>n2>>str1>>n1)
{
solve();
}
return 0;
}