P3592 [POI2015]MYJ
很难的一个题目,比较难想。不过是一个动规题应该还是可以想到的。
考虑如果改变一个洗车店的价格,可能会有很多顾客的选择的会改变,环环相扣,后效性难以避免。为了设计出可以转移的方程,我们必须避免后效性。
注意到一个顾客的选择只会因为他所在的区间中的洗车店的价格变化而变化,所以这个题大概应该是一个区间dp。
为了避免后效性,我们可以想到设$f(i,j,k)$表示在区间$[i,j]$中最便宜的洗车店的价格为$k$时所选范围在$[i,j]$以内的所有顾客提供的最大收益,只有这样,区间才具有可合并性。可以发现设置为题目中出现过的价格时才有最优解,离散化价格即可。
区间dp在合并两个区间时,就可以选择一个中间点$mid$设置为新的最小价格的洗车店,而这个新的最小价格的洗车店只会对左端点在$[i,k-1]$,右端点在$[k+1,j]$的顾客有影响,显然它的价格应该不大于左右两个区间的洗车店的最小价格。
于是我们可以得到转移方程
$f(i,j,k)=max(f(i,pos−1,y)+f(pos+1,j,z)+cnt(pos)∗c[k])$
其中 $y>=k,z>=k$ $cnt(pos)$为区间在,$[i,j]$的$(a,b)$穿过$pos$的数量
(转移方程来自洛谷题解)
cnt无法一次全部预处理出,因为会爆空间,所以只能边处理边转移。
最后还要输出方案,每次更新记录取得最优策略的前驱即可。
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<algorithm>
5 using namespace std;
6 inline int r()
7 {
8 int s=0,k=1;char c=getchar();
9 while(!isdigit(c))
10 {
11 if(c=='-')k=-1;
12 c=getchar();
13 }
14 while(isdigit(c))
15 {
16 s=s*10+c-'0';
17 c=getchar();
18 }
19 return s*k;
20 }
21 int n,m,ans[100001],a[1000001],t,d[1000001],b[1000001],c[1000001],p[1000001],f[55][55][4005],pre[55][55][4005],mid[55][55][4005],cnt[55][4005];//i-j最大超过k时的最大值
22 void dfs(int l,int r,int k)
23 {
24 if(l>r)return;
25 int now=mid[l][r][pre[l][r][k]];//中点
26 ans[now]=d[pre[l][r][k]];
27 dfs(l,now-1,pre[l][r][k]);
28 dfs(now+1,r,pre[l][r][k]);
29 }
30 int main()
31 {
32 n=r();
33 m=r();
34 for(int i=1;i<=m;i++)
35 {
36 a[i]=r();b[i]=r();c[i]=r();
37 d[i]=c[i];
38 }
39 t=m;
40 sort(d+1,d+t+1);
41 t=unique(d+1,d+t+1)-d-1;
42 for(int i=1;i<=m;i++)
43 c[i]=lower_bound(d+1,d+t+1,c[i])-d;
44 //这样是错的因为中间点也必须在a[i]到b[i]之中才可以
45 /* for(int l=1;l<=t;l++)//对于每个人
46 {
47 int left=a[l],right=b[l],price=c[l];
48 for(int i=1;i<=left;i++)
49 for(int j=right;j<=n;j++)
50 for(int k=1;k<=price;k++)
51 {
52 if(i==left&&j==right&&k==price)continue;
53 cnt[i][j][k]+=cnt[left][right][price];
54 }
55 }*/
56 // for(int i=1;i<=n;i++)
57 // for(int j=i;j<=n;j++)
58 // for(int k=1;k<=t;k++)
59 // cout<<"from"<<i<<"to"<<j<<"price"<<d[k]<<"has"<<cnt[i][j][k]<<endl;
60 // for(int len=0;len<=n;len++)
61 // for(int i=1;i<=n;i++)//l
62 // {
63 // int j=i+len;//r
64 // if(j>n)continue;
65
66 for(int i=n;i;i--)
67 for(int j=i;j<=n;j++)
68 {
69
70 for(int k=i;k<=j;k++)
71 for(int l=0;l<=t;l++)
72 cnt[k][l]=0;
73
74 for(int k=1;k<=m;k++)//枚举每个人
75 if(i<=a[k]&&b[k]<=j)
76 for(int l=a[k];l<=b[k];l++)//枚举中间点
77 cnt[l][c[k]]++;
78
79 for(int k=i;k<=j;k++)//下传
80 for(int l=t;l;l--)
81 cnt[k][l]+=cnt[k][l+1];
82
83 for(int k=t;k;k--) //price
84 {
85 int maxi=0;
86 for(int l=i;l<=j;l++)//mid
87 {
88 int tmp=f[i][l-1][k]+f[l+1][j][k]+cnt[l][k]*d[k];
89 if(maxi<=tmp)maxi=tmp,mid[i][j][k]=l;
90 }//最小值选为当前点
91 if(maxi>=f[i][j][k+1])f[i][j][k]=maxi,pre[i][j][k]=k;
92 else f[i][j][k]=f[i][j][k+1],pre[i][j][k]=pre[i][j][k+1];
93 }
94 }
95 cout<<f[1][n][1]<<endl;
96 dfs(1,n,1);
97 for(int i=1;i<=n;i++)
98 cout<<ans[i]<<" ";
99 }
本文来自博客园,作者:lei_yu,转载请注明原文链接:https://www.cnblogs.com/lytql/p/15055574.html