http://codeforces.com/problemset/problem/264/C
题目意思:
n个小球排成一列,每个小球有两个属性:颜色ci,权值vi
然后有q次询问,每次给出两个整数a和b
求一个权值最大的小球子序列:
一个序列的权值这样计算:
当一个小球前面有小球,且颜色相同时,权值为a*vi
否则为b*vi
序列的权值为所有小球权值之和。
长度为0的序列权值视为0,所以本题答案至少为0
很容易写出状态转移方程dp[i]表示以小球i结束的序列的最大权值
dp[i] = max( max(dp[j] + (ci == cj?a:b)*vi) (0<=j<i), b*vi )
1.b*vi 为长度为1的只包含i的序列
2.max(dp[j] + (ci == cj?a:b)*vi) (0<=j<i) 为当有前缀时的最大值
当然,同样的,复杂度为n^2,无法承受。
观察方程,我们可以发现,其实dp[i] = max(颜色与ci不同的j中最大的dp[j]+b*v1, 颜色与ci相同的j中最大的dp[j]+a*v1, b*v1)
那我们何不直接以颜色为状态呢?dp[i]表示以颜色i结束的序列的最大值。
状态方程变为
dp[ci] = max(b*vi, max(dp[j],j!=ci) + b*vi, dp[ci] + a*vi, dp[ci])
复杂度还是n^2没有优化的地方吗?
状态转移费用主要耗费在求max(dp[j] | j!=ci)上,我们何不干脆记录当前dp[0..n]的最大值呢?不过万一最大值的i正好与当前的ci相同怎么办?那就是次大的!
所以记录最大的和次大dp[i]即可完美解决问题!
当ci与最大的dp[i]颜色相同时,返回次大值,否则返回最大值。同时,每次更新后,更新一下最大值和次大值。
转移费用马上下降到了1,问题解决!
注意刚开始把所有dp[i]置为负无穷大,而不是0
代码如下:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 typedef long long ll; 7 const int N=100100; 8 const ll inf=~0ull>>3; 9 const ll finf=-inf; 10 int n,c[N],v[N]; 11 ll a,b,dp[N]; 12 struct poi{ 13 ll mx1,mx2; 14 int mxc1,mxc2; 15 void cl(){ 16 mxc1=mxc2=-1; 17 } 18 ll getmax(int c){ 19 if(mxc1 == -1) return finf; 20 else if(c!=mxc1) return mx1; 21 else if(mxc2 == -1) return finf; 22 else return mx2; 23 } 24 void add(int c,ll mx){ 25 if(mxc1==-1){ 26 mxc1=c; 27 mx1=mx; 28 } 29 else if(c==mxc1){ 30 if(mx>mx1) mx1=mx; 31 } 32 else if(mx>mx1){ 33 mxc2=mxc1; 34 mx2=mx1; 35 mxc1=c; 36 mx1=mx; 37 } 38 else if(mxc2==-1 || (mx > mx2)){ 39 mxc2=c; 40 mx2=mx; 41 } 42 } 43 }; 44 void solve(){ 45 for(int i=0;i<n;i++)dp[i]=finf; 46 dp[c[0]]=b*v[0]; 47 poi pmx; 48 pmx.cl(); 49 pmx.add(c[0],b*v[0]); 50 for(int i=1;i<n;i++){ 51 ll tmp=pmx.getmax(c[i]) + b*v[i]; 52 ll tmp2=dp[c[i]] + a*v[i]; 53 tmp=max(tmp,tmp2); 54 tmp2=b*v[i]; 55 tmp=max(tmp,tmp2); 56 if(tmp > dp[c[i]]){ 57 dp[c[i]]=tmp; 58 pmx.add(c[i],tmp); 59 } 60 } 61 ll ans=0; 62 for(int i=0;i<n;i++)if(dp[i]>ans) ans=dp[i]; 63 printf("%I64d\n",ans); 64 } 65 int main() 66 { 67 int m; 68 while (~scanf("%d%d",&n,&m)) 69 { 70 for(int i=0;i<n;i++)scanf("%d",v+i); 71 for(int i=0;i<n;i++){ 72 scanf("%d",c+i); 73 c[i]--; 74 } 75 while(m--){ 76 scanf("%I64d%I64d",&a,&b); 77 solve(); 78 } 79 } 80 return 0; 81 }