UVA 1625 颜色长度

题目描述:入门经典 p276

先输入一个整数n表示查询多少次,后面接n对颜色串(一共2n条)这是一个比较特殊的动态规划,哪特殊呢?它比较的对象不同,标准不一样,比如01背包比较的标准是让价值最大化,价值是可以直接算出来的,而这条题是比较L©和最小,这是不可以直接算出来的,要间接求解来比较,理解了这个间接求解,那么下面就很简单了
想法:用 f[i] [j] ,表示从A颜色串拿i 个字符,B颜色串拿 j 个字符;
num[i][j] 表示从A,B颜色串中拿i j个字符来排列,具体顺序我们先不管,我们只知道这个意义:它代表的是这个序列中有多少个未排完的元素,就是说该L©还是未定的,后面还有相同的颜色。 然后设sum为所有L©的和,那么你会发现如果再加入一个元素,那么sum的值会加上num[i][j] ,我们就是通过这个来比较大小的。间接法来求比较的标准。
所以我们有状态转移方程: f[i][j]=min(f[i][j-1],f[i-1][j])+num[i][j]
这样我们又要求出所有num[i][j],那么如何来确定这个num[i][j]
实际上 num[i][j]=num[i][j-1]+judge(j)judge表示j这个B[j],加入后B[j]所代表的颜色是否到了终点,是则-1,如果B[j]是"新来的"那么+1,再不然为0.接下来就可以愉快的写代码了,f[i][j]用了优化空间的方法,如果不知道,可以去其他地方找找背包的空间优化的问题。另外我用M[200]来统计所有颜色的个数,用其他如m[200],aa[200],bb[200]来统计目前序列中颜色个数,代码如下:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int  M[200];//可以理解为大地图,所有颜色的信息
char A[5005],B[5005];
int f[5005];
int num[5005][5005];
void pn(int a,int b)//调试用的函数,可删
{
 for(int i=0;i<=a;i++)
 {
  for(int j=0;j<=b;j++)
   cout<<num[i][j]<<" ";
  cout<<endl;
  }
}
int main()
{
 int n;
 cin>>n;
 while(n--)//询问n次
 { memset(M,0,sizeof(M));
  cin>>A>>B;
  int a,b,i;
  int aa[200]={},bb[200]={};
  for(i=0;A[i]!='\0';i++)//统计A颜色串的个数,顺便求其长度
    M[A[i]]++;
  a=i;
  for(i=0;B[i]!='\0';i++)//上同
    M[B[i]]++;
  b=i;
num[0][0]=0;//所有的数从这里推导,因为要询问多次,所以必须滴
  for(i=0;i<a;i++)//先把所有num[i][0]给弄了
  {
   aa[A[i]]++;
   if(aa[A[i]]==1)//新来的
    num[i+1][0]=num[i][0]+1;
   else if(aa[A[i]]==M[A[i]])//结束了
    num[i+1][0]=num[i][0]-1;
   else //什么也没发生
    num[i+1][0]=num[i][0];
   //cout<<aa[A[i]]<<"  "<<num[i+1][0]<<"  "<<A[i]<<"  "<<M[A[i]]<<endl;
   }
  for(i=0;i<b;i++)//再把所有num[0][i]给弄了
  {
   bb[B[i]]++;
   if(bb[B[i]]==1)
    num[0][i+1]=num[0][i]+1;
   else if(bb[B[i]]==M[B[i]])
    num[0][i+1]=num[0][i]-1;
   else 
    num[0][i+1]=num[0][i];
   }
  int m[200]={};
  for(int i=1;i<=a;i++)//开始求剩下的num[i][j]
  {
   m[A[i-1]]++;
   for(int j=1;j<=b;j++)
   {
    m[B[j-1]]++;
    int val=0;
    if(m[B[j-1]]==1)
     val=1;
    else if(m[B[j-1]]==M[B[j-1]])
     val=-1;
    
    num[i][j]=num[i][j-1]+val;
    }
   for(int j=0;j<b;j++)
    m[B[j]]--;
   }
 // pn(a,b);
  f[0]=0;//接下来就是简单的DP了
 // cout<<endl<<endl;
  for(int i=1;i<=b;i++)//设定0行的初始值
   f[i]=f[i-1]+num[0][i];
  for(int i=1;i<=a;i++){
   for(int j=0;j<=b;j++)
   {
    
    if(j) 
     f[j]=min(f[j-1],f[j])+num[i][j];
    else 
     f[j]+=num[i][j]; 
    //cout<<f[j]<<" ";
    }
   // cout<<endl;
   }
  cout<<f[b];
  }
  
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值