2016"百度之星" - 复赛(Astar Round3)1001 D++游戏 hdu 5712

这道题一看当时一猜不是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;
}


 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值