这道题一看当时一猜不是dp就是搜
但是无论是搜还是dp都要解决一个问题就是
一个序列被删除后剩下的两部分是连在一起的,这个时候怎么搞
当时我想的是枚举一开始的等差数列那么再去求等差数列的相邻两项的值就行,这样就不会存在不连在一起的情况来了
我当时很确定这个想法入口是很正确的,但是我一直想的是去搜每个小区间,但是我一直想不出来怎么搜,最后和别人聊天去了
后来看了别人的代码,恍然大雾,以后写题不要放弃啊==,我感觉我开始的思路很正确
多思考方法说不定就出来,不过这道题的处理方式确实是属于较难的dp了,dp还是太弱
大致思路:
dp[i][j]表示删除i-j区间最少的次数
求得这个dp后
ans[i]:表示区间1-i的最优取值情况
我们利用这两个状态就可以求得最优解了
dp的求解方式:
既然说到了要枚举相邻的等差数列
那么我们可以枚举一个大的等差数列区间,然后dp的时候向中间减小范围(枚举第一个的下一个
然后我们就得到了一个独立的区间和一个等差数列长度减一的区间
这个过程要开两个dp然后相互更新
具体键代码
<pre name="code" class="cpp">/* ^^ ====== ^^
ID: meixiuxiu
PROG: test
LANG: C++11
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#include <cctype>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int ,int> pii;
#define MEM(a,b) memset(a,b,sizeof a)
#define CLR(a) memset(a,0,sizeof a);
#define pi acos(-1.0)
#define maxn 40000
#define maxv 100005
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
//#define LOCAL
int dp[50][50][50];
int ans[50][50];
int n,m,nmin,nmax;
int a[50],d[50];
pii res[50];
bool check(int tmp){
for(int i=1;i<=m;i++)
if(d[i]==tmp)return 1;
return 0;
}
pii max(pii a,pii b){
if(a.first > b.first)return a;
else if(a.first == b.first && a.second < b.second)return a;
else return b;
}
pii add(pii a,pii b){
pii c;
c.first = a.first + b.first;
c.second = a.second + b.second;
return c;
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
int t;cin >> t;
int kase =1;
while(t--){
printf("Case #%d:\n",kase++);
cin >> n >> m >> nmin >> nmax;
for(int i=1;i<=n;i++)cin >> a[i];
for(int i=1;i<=m;i++)cin >> d[i];
//枚举区间
MEM(dp,inf);
for(int i=0;i<=m;i++)
for(int j=0;j<=n;j++)dp[i][j+1][j] = 0,ans[j+1][j] = 0;
for(int len=1;len<=n;len++){
for(int start = 1;start+len-1 <= n;start++){
//枚举这个区间要删除的个数,l为第一个要删除的数,r为最后一个
int l = start, r = start+len-1;
ans[l][r] = inf;
for(int i=1;i<=nmax;i++){
//要删除的为1,那么r被删除
dp[i][l][r] = inf;
if(r-l+1 < i)continue;
if(i==1)dp[i][l][r] = min(dp[i][l][r],ans[l+1][r]+1);
else{
//枚举等差数列当前的第一个
if((a[r]-a[l])%(i-1))continue;
int d = (a[r]-a[l])/(i-1);
if(!check(d))continue;
for(int p=l+1;p<=r;p++){
if(a[p]-a[l]==d)
dp[i][l][r] = min(dp[i-1][p][r]+ans[l+1][p-1],dp[i][l][r]);
}
if(i>=nmin && i <= nmax){
ans[l][r] = min(dp[i][l][r],ans[l][r]);
}
}
}
for(int i=l;i<=r;i++)ans[l][r] = min(ans[l][r],ans[l][i]+ans[i+1][r]);
}
}
pii mmax = pii(0,0);
res[0] = pii(0,0);
for(int i=1;i<=n;i++){
res[i] = res[i-1];
for(int j=1;j<=i;j++){
if(ans[j][i]==inf)continue;
res[i] = max(res[i],add(res[j-1],pii(i-j+1,ans[j][i])));
}
}
cout << res[n].first << ' ' << res[n].second << endl;
}
return 0;
}