区间DP合集

①HDU5115:http://acm.hdu.edu.cn/showproblem.php?pid=5115

题意:每只狼有自身攻击和额外攻击两个属性,杀手杀死它时受到它自身攻击(ai)和相邻两只(一只)狼的额外攻击(b(i-1) , b(i+1))之和,杀死之后相邻关系会对应调整,

求杀手杀死全部狼受到的最少伤害。

思路:一开始一直想贪心,结果还是放弃了.....

区间DP,dp[i][j]表示杀死第i~j只狼所受到的最小伤害,枚举k(k∈[ i , j ] )为最后一只被杀死的狼求出dp[i][j]。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 203;
const int inf = 0x3f3f3f3f;
int a[N],b[N],dp[N][N];
int main()
{
    int T,cas = 1;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));
        for(int i = 1; i <= n; i++)
        {
            for(int j = i; j <= n; j++)
                dp[i][j] = inf;
        }
        a[0] = a[n+1] = b[0] = b[n+1] = 0;
        for(int i = 1; i <= n; i++)
            scanf("%d",a+i);
        for(int i = 1; i <= n; i++)
            scanf("%d",b+i);
        for(int i = n; i >= 1; i--)
        {
            for(int j = i; j <= n; j++)
            {
                for(int k = i; k <= j; k++)
                    dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+a[k]+b[i-1]+b[j+1]);//dp[i][k-1],dp[k+1][j]必须为已知,故第一、二层循环分别为倒序和正序
            }
        }
        printf("Case #%d: %d\n",cas++,dp[1][n]);
    }
    return 0;
}



②HDU5900:http://acm.hdu.edu.cn/showproblem.php?pid=5900

题意:给定N个二元组(含有key和value),相邻且gcd不为1的两个二元组可以消去,之后旁边两个二元组变成相邻的。

思路:明显的区间DP,如果从i+1~j-1都可以被消去,则dp[i][j] = dp[i+1][j-1]+value[i]+value[j]。

判断区间是否可以被消去,可以建一个前缀数组sum,如果sum[j-1]-sum[i] = dp[i+1][j-1]即等于区间内所有值之和,说明可以被消去。

#include<bits/stdc++.h>  
using namespace std;  
typedef long long ll;  
const int N = 305;  
ll gcd(ll a,ll b)  
{  
    return b ? gcd(b, a % b) : a;  
}  
int n;  
ll key[N],val[N],sum[N],dp[N][N];  
void init()  
{  
    memset(dp,0,sizeof(dp));  
    memset(sum,0,sizeof(sum));  
}  
int main()  
{  
    int T;  
    scanf("%d",&T);  
    while(T--)  
    {  
        init();  
        scanf("%d",&n);  
        for(int i = 1; i <= n; i++)  
            scanf("%lld",key+i);  
        for(int i = 1; i <= n; i++)  
        {  
            scanf("%lld",val+i);  
            sum[i] = sum[i-1] + val[i];  
        }  
        for(int i = n - 1; i >= 1; i--)  
        {  
            for(int j = i + 1; j <= n; j++)  
            {  
                if(gcd(key[i],key[j]) > 1 && sum[j-1] - sum[i] == dp[i+1][j-1])  
                {  
                    dp[i][j] = max(dp[i][j],sum[j] - sum[i-1]);  
                    continue;  
                }  
                for(int k = i; k < j; k++)  
                    dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j]);  
            }  
        }  
        printf("%lld\n",dp[1][n]);  
    }  
    return 0;  
}



③HDU4283:http://acm.hdu.edu.cn/showproblem.php?pid=4283

题意:有N个屌丝,屌丝值为a[i],第k个出场的屌丝unhappy值为(k-1)*a[i],导演可以将一些屌丝关进小黑屋(栈)中,在适当的时候再让他们出来,以调整顺序。问怎么调整出场顺序使得总unhappy值最小。

思路:能想到是DP,但想不到是区间DP =_= 。。。dp[i][j]表示区间[ i , j ]的屌丝出场的最小unhappy值,则可得到方程:

dp[i][j] = min(dp[i][j],dp[i+1][k] + dp[k+1][j] + (k - i + 1)*(sum[j] - sum[k]) + (k - i)*a[i])

表示在k之后i出场(k == i表示i第一个出场),由于i的插入,在i之后出场的相比之前([i+1,j])需要增加(k - i + 1)倍该区间的屌丝值,再加i本身(k-i)*a[i]的unhappy值。

#include<bits/stdc++.h>  
using namespace std;  
const int N = 103;  
const int inf = 0x3f3f3f3f;  
int a[N],dp[N][N],sum[N];  
int main()  
{  
    int T,cas = 1;  
    scanf("%d",&T);  
    while(T--)  
    {  
        int n;  
        scanf("%d",&n);  
        for(int i = 1; i <= n; i++)  
        {  
            scanf("%d",a+i);  
            sum[i] = sum[i-1] + a[i];  
            for(int j = i; j <= n; j++)  
                dp[i][j] = inf;  
        }  
        for(int i = n; i >= 1; i--)  
        {  
            for(int j = i; j <= n; j++)  
            {  
                for(int k = i; k <= j; k++)  
                    dp[i][j] = min(dp[i][j],dp[i+1][k] + dp[k+1][j] + (k - i + 1)*(sum[j] - sum[k]) + (k - i)*a[i]);  
            }  
        }  
        printf("Case #%d: %d\n",cas++,dp[1][n]);  
    }  
    return 0;  
}




④:HDU5693:http://acm.hdu.edu.cn/showproblem.php?pid=5693

思路:任何大于1的数都可以拆成2x+3y的形式,所以只需要考虑删除2或3个元素的情况。

#include<bits/stdc++.h>  
using namespace std;  
typedef long long ll;  
const int N = 303;  
int dp[N][N],a[N];  
bool yes[N][N];  
int main()  
{  
    int T;  
    scanf("%d",&T);  
    while(T--)  
    {  
        int n,m;  
        scanf("%d %d",&n,&m);  
        memset(dp,0,sizeof(dp));  
        memset(yes,0,sizeof(yes));  
        for(int i = 1; i <= n; i++)  
            scanf("%d",a+i);  
        for(int i = 1; i <= m; i++)  
        {  
            int x;  
            scanf("%d",&x);  
            for(int j = 1; j <= n; j++)  
            {  
                for(int k = j + 1; k <= n; k++)  
                {  
                    if(a[k] - a[j] == x)  
                        yes[j][k] = 1;  
                }  
            }  
        }  
        for(int i = n - 1; i >= 1; i--)  
        {  
            for(int j = i + 1; j <= n; j++)  
            {  
                if(yes[i][j] && dp[i+1][j-1] == j - i - 1)//中间元素都已经被删除  
                    dp[i][j] = j - i + 1;  
                for(int k = i + 1; k < j; k++)  
                {  
                    int f1 = 0,f2 = 0;  
                    if(yes[i][k] && dp[i+1][k-1] == k - i - 1)//删除i,k两个元素  
                        dp[i][j] = max(dp[i][j],k - i + 1 + dp[k+1][j]), f1 = 1;  
                    if(yes[k][j] && dp[k+1][j-1] == j - k - 1)//删除k,j两个元素  
                        dp[i][j] = max(dp[i][j],j - k + 1 + dp[i][k-1]), f2 = 1;  
                    if(f1 && f2 && a[j] - a[k] == a[k] - a[i])//删除i,j,k三个元素  
                        dp[i][j] = max(dp[i][j],j - i + 1);  
                    dp[i][j] = max(dp[i][j],dp[i][k] + dp[k+1][j]);  
                }  
            }  
        }  
        printf("%d\n",dp[1][n]);  
    }  
    return 0;  
}




⑤HDU5151:http://acm.hdu.edu.cn/showproblem.php?pid=5151

题意:有n个人,n把椅子有红蓝两种颜色,n个人按顺序依次选座位,当一把椅子相邻的两把椅子非空且颜色不同时不能入座,问有几种每个人都能选到座位的方法。

思路:dp[i][j]表示对区间[i, j]的椅子有多少不同的方案,枚举k作为区间中最后一个被选的位置。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 103;
const int mod = 1e9 + 7;
ll dp[N][N],c[N][N];
int a[N];
int main()
{
    int n;
    c[0][0] = 1;
    for(int i = 1; i <= 100; i++)
    {
        c[i][0] = 1;
        for(int j = 1; j <= i; j++)
            c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;//组合数递推公式
    }
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        for(int i = 1; i <= n; i++)
            scanf("%d",a+i);
        for(int i = n; i >= 1; i--)
        {
            dp[i][i] = 1;
            for(int j = i + 1; j <= n; j++)
            {
                for(int k = i; k <= j; k++)
                {
                    if(k == i)
                        dp[i][j] = (dp[i][j] + dp[k+1][j]) % mod;
                    else if(k == j)
                        dp[i][j] = (dp[i][j] + dp[i][k-1]) % mod;
                    else
                    {
                        if(a[k-1] == a[k+1])
                            dp[i][j] = (dp[i][j] + c[j-i][k-i] *dp[i][k-1] % mod * dp[k+1][j] % mod) % mod;//在j-i个人中选k-i个人坐在k左侧,其余坐右侧
                    }
                }
            }
        }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}




⑥HDU4293:http://acm.hdu.edu.cn/showproblem.php?pid=4293

题意:一开始理解错了,以为询问回答的是自己在自己队伍里的位置,其实应该是自己队伍的前后各有多少人,问最多有多少人说的是真话。

思路:又是面向题解...dp[i][j]表示区间[i , j]的人为一组时[i , j]内最多有多少人说真话,如果a + b  + 1> n显然是假的,并且dp[i][j]不能超过其区间长度,ans[i]表示前i个人中最多多少人说真话。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 503;
int ans[N],dp[N][N];
int main()
{
    int n,a,b;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        memset(ans,0,sizeof(ans));
        for(int i = 1; i <= n; i++)
        {
            scanf("%d %d",&a,&b);
            if(a + b + 1 <= n && dp[a+1][n-b] < n - a - b)
                dp[a+1][n-b]++;
        }
        for(int j = 1; j <= n; j++)
        {
            for(int i = 0; i < j; i++)
                ans[j] = max(ans[j],ans[i] + dp[i+1][j]);
        }
        printf("%d\n",ans[n]);
    }
    return 0;
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值