题意:
开始的时候有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;
}