两个长度分别为n,m的序列,序列中均为不超过100000的正数,现有两种操作,操作1消耗e点能量,可以选择两个序列的前缀,保证这两个前缀的最后一个数相同,然后删去这两个前缀,分数+1;操作2消耗当前两个序列中被删掉的数的个数,然后清空两个序列,得到由操作1累加起来的分数。 现在给定两个序列和总能量s以及操作1消耗的能量e,求最大的分数。
这题入手点是e和s的范围,有这两个范围我们可以确定操作1最多进行300次,而且每次从序列a中选一个数,要在序列b中找一个位置与他相同,这个位置一定越靠前越好(靠前删掉的数少,而且不会影响之后的情况),那么可以用dp[v][i]来表示第v次操作,序列a选择的位置小于等于i时,序列b中选择的位置,转移方程dp[v][i]=min(dp[v][i],ps),ps为[dp[v-1][i-1]+1,n]中最靠前的位置,使得a[i]==b[ps].,实现的话可以用10W个vector记录一下b中每个数出现的位置,那么在找ps时就可以通过二分查找来实现..自己手写了一个二分搜索结果T掉了,换成系统的Lower_bound就过了...果然还是系统的函数强....
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> using namespace std; typedef long long ll; int dp[320][110000]; int a[110000],b[110000]; const int inf=10000000; int n,m,s,e; vector<int> g[110000]; int main() { // freopen("in.txt","r",stdin); while(~scanf("%d%d%d%d",&n,&m,&s,&e)) { for (int i=1; i<=n; i++) scanf("%d",&a[i]); for (int i=0; i<=100000; i++) g[i].clear(); for (int j=1; j<=m; j++) { scanf("%d",&b[j]); g[b[j]].push_back(j); } for (int i=0; i<=100000; i++) g[i].push_back(inf); memset(dp,0x3f,sizeof dp); memset(dp[0],0,sizeof dp[0]); // memset(minn,0x3f,sizeof minn); // memset(id,-1,sizeof id); // id[0]=0; // minn[0]=0; for (int i=1; i<301; i++) { for (int j=i; j<=n; j++) { if (dp[i-1][j-1]<inf) { dp[i][j]=dp[i][j-1]; // int ps=search(g[a[j]],dp[i-1][j-1]+1); int ps=*lower_bound(g[a[j]].begin(),g[a[j]].end(),dp[i-1][j-1]+1); dp[i][j]=min(dp[i][j],ps); } } } int ans=0; for (int i=1; i<301; i++) for (int j=i; j<=n; j++) if (dp[i][j]<inf && (j+dp[i][j])+i*e<=s) { ans=max(ans,i); } printf("%d\n",ans); } return 0; }