UVA1625 - Color Length (DP)

来源:互联网 发布:python cv 读取图片 编辑:程序博客网 时间:2024/04/27 20:40

题目链接 UVA1625 (题目看不到请看PDF)

 【题意】

 

【分析】

 

我是用dp[i][j]表示第一个序列移除i个元素,第二个序列移除j个元素时候的最小的权值。

然后对于怎样在o(1)时间内计算出移除当前元素的费用,想了两天也没想出来,最后还是看了别人的代码才明白的。唉,DP弱啊。

其实只要用两个数组f[]来记录每个元素在每个序列是否已经出现即第一次出现后一直标记为1,其他标记为0;用g[][]来记录当前元素在当前序列是否已经结束,即结束后的位置都标记为0,其他标记为1;也就是用f[][]记录每个元素在当前序列的开始位置,g[][]记录每个元素在当前序列的结束位置;两个数组不能用一个来表示,怎样没法判断,下面会说;

这样记录的话直接可以在O(1)时间判断当前去掉的元素是不是开始和结束位置。例如:当前已经在第一个序列移除了i个元素,第二个序列已经移除了j个元素,如果现在要移除第一个序列的第i+1个元素,那么只要判断i+1个元素的f[0][i]是否为0(如果为0的话那么当前元素在第一个序列第i+1个位置肯定是首次出现,因为已经把后面所有f[][]全部标记为1了,而当前既然已经是这个元素,那么当前f[0][i+1]必定是1,再判断i+1的元素在第二个序列是否已经出现f[1][j];如果f[0][i]和f[1][j]都为0说明这个元素没有没删除过),同样可以通过i+1后一个位置g[][]是不是0来判断i+1的元素是不是已经结束了。

有了每个元素在每个序列的开始和结束位置,以及当前状态在去掉某个元素是否是第一次删除以及最后一次删除;

每个状态只由两个状态dp[i-1][j]或者dp[i][j-1]转移过来,当选进来的元素是第一次出现的时候,权值减去当前坐标(i+j),当这种元素在剩下未选的元素中不会再出现时,权值加上(i+j)

【AC代码】76ms

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define MAXN 5005struct NODE{//f[]记录每个字母是否已经出现,g[]记录每个字母是否已经结束bool f[2][MAXN], g[2][MAXN];}s[30];char a[2][MAXN];int dp[MAXN][MAXN], len[2];void find()//寻找每个字母在两个序列首次出现和结束的位置{for (int i = 0; i < 26; i++){for (int k = 0; k < 2; k++){int st = -1, ed = -1;for (int j = 0; j < len[k]; j++){if (i + 'A' == a[k][j]){st = j;break;}}for (int j = len[k] - 1; j >= 0; j--){if (i + 'A' == a[k][j]){ed = j;break;}}if (-1 != st){for (int j = st + 1; j <= len[k]; j++)s[i].f[k][j] = 1;for (int j = 1; j <= ed + 1; j++)s[i].g[k][j] = 1;}}}}int main(){#ifdef SHYfreopen("e:\\1.txt", "r", stdin);#endifint t;scanf("%d%*c", &t);while (t--){gets(a[0]), len[0] = strlen(a[0]);gets(a[1]), len[1] = strlen(a[1]);memset(s, 0, sizeof(s));memset(dp[0], 0x3f, sizeof(dp[0]));dp[0][0] = 0;find();for (int i = 0; i <= len[0]; i++){memset(dp[i + 1], 0x3f, sizeof(dp[i + 1]));//直接对dp[][]一起赋值会超时for (int j = 0; j <= len[1]; j++){if (i < len[0])//在第一个序列移除i+1的时候{int t = dp[i][j], p = a[0][i] - 'A';if (!s[p].f[0][i] && !s[p].f[1][j]) t -= i + j;//当前字母p是不是第一次被移除if (!s[p].g[0][i + 2] && !s[p].g[1][j + 1]) t += i + j;//当前字母是不是最后一次被移除dp[i+1][j] = min(dp[i+1][j], t);}if (j < len[1])//在第二个序列移除j+1的时候{int t = dp[i][j], p = a[1][j] - 'A';if (!s[p].f[0][i] && !s[p].f[1][j]) t -= i + j;if (!s[p].g[0][i + 1] && !s[p].g[1][j + 2]) t += i + j;dp[i][j + 1] = min(dp[i][j + 1], t);}}}printf("%d\n", dp[len[0]][len[1]]);}return 0;}


 

【状态压缩+滚动数组后AC代码】29ms

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define MAXN 5005#define to(x) (1<<(x))char a[MAXN], b[MAXN];int dp[2][MAXN], lena, lenb, f[2][MAXN], g[2][MAXN];//f[]记录每个字母是否已经出现,g[]记录每个字母是否已经结束int main(){#ifdef SHYfreopen("e:\\1.txt", "r", stdin);#endifint t;scanf("%d%*c", &t);while (t--){gets(a), lena = strlen(a);gets(b), lenb = strlen(b);memset(dp, 0x3f, sizeof(dp));g[0][lena + 1] = g[1][lenb + 1] = dp[0][0] = 0;for (int i = 1; i <= lena; i++){f[0][i] = f[0][i - 1] | to(a[i-1]-'A');g[0][lena - i + 1] = g[0][lena - i + 2] | to(a[lena-i]-'A');}for (int i = 1; i <= lenb; i++){f[1][i] = f[1][i - 1] | to(b[i-1]-'A');g[1][lenb - i + 1] = g[1][lenb - i + 2] | to(b[lenb-i]-'A');}int now = 1, next = 0;for (int i = 0; i <= lena; i++){now = !now, next = !next;memset(dp[next],0x3f,sizeof(dp[next]));for (int j = 0; j <= lenb; j++){if (i < lena){int t = dp[now][j];if (~(f[0][i] | f[1][j])&to(a[i] - 'A')) t -= i + j;if (~(g[0][i + 2] | g[1][j + 1])&to(a[i] - 'A')) t += i + j;dp[next][j] = min(dp[next][j], t);}if (j < lenb){int t = dp[now][j];if (~(f[0][i] | f[1][j])&to(b[j] - 'A')) t -= i + j;if (~(g[0][i + 1] | g[1][j + 2])&to(b[j] - 'A')) t += i + j;dp[now][j + 1] = min(dp[now][j+1], t);}}}printf("%d\n", dp[now][lenb]);}return 0;}



 

1 0
原创粉丝点击