zoj 3375 Imperishable Night (状压dp)

题意:

开始的时候有point,lv,tv.都是0。每个洞穴有四个值ai,bi,xi,yi, xi表示,yi分别表示有a类xi个,b类yi个。如果选择a类那么point+=lv,tv+=ai,如果选择b类point+=tv,lv+=bi;

问,如何得到最大的值point。

题解:

状压dp,分别对整体状压dp,和对每个洞内部dp,对洞内部其实用贪心也可以,复杂度O(1)。

分析:

首先,对于每个part,设如果在IV和TV在这个part中初始都为0时,在这个part中能收集到的最大的能量为gao[i]。再设进入这个part之前的IV值和TV值分别为IV0和TV0,那么在这个part中实际能得到的最大能量为IV0 * x[i] + TV0 * y[i] + gao[i]。而每个part进行完后,IV和TV肯定是分别增加了b[i] * y[i]和a[i] * x[i],也就是说在只要知道在进入这个part之前,已经完成了哪些part,连顺序都不用考虑,就可以知道IV0和TV0的值。因为n≤15,可以使用状态压缩的DP来求解。


再考虑如何算出gao[i]。可以用dp的方法,设dp[x][y]为收集了x个ITEM和y个TIME能得到的最大能量。那么就有dp[x][y] = max(dp[x - 1][y] + b[i] * y, dp[x][y - 1] + a[i] * x)。于是有gao[i] = dp[x[i]][y[i]],这个时间复杂度为x[i] * y[i]。


事实上直接贪心即可!不妨设某个part中a[i] > b[i],那么收集方法应该是先把所有的ITEM收集,再把所有的TIME收集。反之亦然。证明如下:设一个收集序列为*****TI*****,T代表收集了TIME,I代表收集了ITEM,那么通过交换相邻的T和I的,一定可以使得到能量增加。设收集这个T之前,IV和TV分别为IV1和TV1,那么按照TI的顺序,得到的能量为TV1 + (IV1 + b[i]),收集完这两个后,IV = IV1 + b[i], TV = TV1 + a[i]。如果按照IT的顺序,收集完这两个后,IV = IV1 + b[i], TV = TV1 + a[i]是一样的,但是得到的能量就是IV1 + (TV1 + a[i]),比TI更多。因此,凡是遇到TI,就变为IT,这样一来,最优的顺序肯定是IIIII……TTTTT。于是有gao[i] = max(a[i], b[i]) * x[i] * y[i],这个时间复杂度是O(1)的。

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<stdlib.h>
#include<ctype.h>
using namespace std;
typedef long long lld;
#define oo 0x3f3f3f3f
#define maxn 1005
int dp[maxn][maxn];
int list[maxn][maxn];
int n, m;

int max(int a, int b, int c)
{
    return max(a, max(b, c));
}

void GetList()
{
    list['A']['A'] = 5; list['A']['C'] = -1; list['A']['G'] = -2; list['A']['T'] = -1; list['A']['-'] = -3;
    list['C']['A'] = -1; list['C']['C'] = 5; list['C']['G'] = -3; list['C']['T'] = -2; list['C']['-'] = -4;
    list['G']['A'] = -2; list['G']['C'] = -3; list['G']['G'] = 5; list['G']['T'] = -2; list['G']['-'] = -2;
    list['T']['A'] = -1; list['T']['C'] = -2; list['T']['G'] = -2; list['T']['T'] = 5; list['T']['-'] = -1;
    list['-']['A'] = -3; list['-']['C'] = -4; list['-']['G'] = -2; list['-']['T'] = -1; list['-']['-'] = -oo;
}

int main()
{
    GetList();
    int n, m, T;
    char a[maxn], b[maxn];
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d %s", &n, a);
        scanf("%d %s", &m, b);

        dp[0][0] = 0;
        for (int i = 1; i <= n; i++)
            dp[i][0] = dp[i - 1][0] + list[a[i - 1]]['-'];
        for (int i = 1; i <= m; i++)
            dp[0][i] = dp[0][i - 1] + list['-'][b[i - 1]];
        for (int i = 1; i <= n;i++)
        for (int j = 1; j <= m; j++)
        {
            dp[i][j] = max(dp[i - 1][j - 1] + list[a[i - 1]][b[j - 1]],
                dp[i - 1][j] + list[a[i - 1]]['-'],
                dp[i][j - 1] + list['-'][b[j - 1]]);
        }
        printf("%d\n", dp[n][m]);
    }
    return 0;
}





  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值