T1.成绩单
期末考试结束了,班主任 老师要将成绩单分发到每位同学手中。
老师共有份成绩单,按照编号从到的顺序叠放在桌子上,其中编号为的成绩单分数为。
成绩单是按照批次发放的。发放成绩单时,老师会从当前的一叠成绩单中抽取连续的一段,让这些同学来领取自己的成绩单。
当这批同学领取完毕后,老师再从剩余的成绩单中抽取连续的一段,供下一批同学领取。
经过若干批次的领取后,成绩单将被全部发放到同学手中。
然而,分发成绩单是一件令人头痛的事情,一方面要照顾同学们的心理情绪,不能让分数相差太远的同学在同一批领取成绩单;
另一方面要考虑时间成本,尽量减少领取成绩单的批次数。
对于一个分发成绩单的方案,我们定义其代价为:
\(a*k+b*\sum_{i=1}^k(max_i-min_i)^2\)
其中,是方案中分发成绩单的批次数,对于第批分发的成绩单,是最高分数,是最低分数。
是给定的评估参数。现在,请你帮助老师找到代价最小的分发成绩单的方案,并将这个最小的代价告诉老师。
当然,分发成绩单的批次数是由你决定的。
一道题面比较真实的题目
需要离散化,设状态\(f[i][j][l][r]\)为在[i,j]表示\([i,j]\)区间内取到剩余数字值域为区间\([l,r]\)的最小代价。特别的,令\(f[i][j][0][0]\)表示\([i,j]\)区间取完的最小代价。枚举一个中间点\(k\),将\([l,r]\)分为\([l,k]\)和\([k+1,r]\)两个区间进行状态转移取最小值。而显然
\(f[i][j][0][0]=min(f[i][j][l][r]+a+b*(r-l)^2)\)。
注意如果是dfs遍历的话不要忘了加记忆化
#include <bits/stdc++.h>
using namespace std;
#define N 55
#define INF 2000000000
#define int long long
//懒得一个一个longlong改过去了...
inline int rf()
{
int r;int s=0,c;
for(;!isdigit(c=getchar());s=c);
for(r=c^48;isdigit(c=getchar());
(r*=10)+=c^48);
return s^45?r:-r;
}
int n,a,b,top=1;
int w[N],ww[N];
int ans[N][N];
int f[N][N][N][N];
bool vis[N][N][N][N];
int dfs(int i,int j,int l,int r)
{
if(vis[i][j][l][r]) return f[i][j][l][r];
f[i][j][l][r]=INF,vis[i][j][l][r]=1;
if(l==0&&r==0)//对f[i][j][0][0]进行转移
{
int minn=top+1,maxx=0;
for(int k=i;k<=j;k++)
minn=min(minn,w[k]),maxx=max(maxx,w[k]);
for(int k=1;k<=top;k++)
for(int m=k;m<=top;m++)
{
int res=dfs(i,j,k,m);
if(res==INF) continue;
f[i][j][l][r]=min(f[i][j][l][r],res+a+b*(ww[m]-ww[k])*(ww[m]-ww[k]));
}
}
else//就是普通的f[i][j][l][r]
{
if(i==j)
{
if(l<=w[i]&&w[i]<=r) f[i][j][l][r]=0;
return f[i][j][l][r];
}
for(int k=i;k<j;k++)
{
f[i][j][l][r]=min(f[i][j][l][r],dfs(i,k,0,0)+dfs(k+1,j,l,r));
f[i][j][l][r]=min(f[i][j][l][r],dfs(i,k,l,r)+dfs(k+1,j,0,0));
f[i][j][l][r]=min(f[i][j][l][r],dfs(i,k,l,r)+dfs(k+1,j,l,r));
}
}
return f[i][j][l][r];
}
signed main()
{
n=rf();
a=rf(),b=rf();
for(int i=1;i<=n;i++)
w[i]=rf(),ww[i]=w[i];
sort(ww+1,ww+n+1);
for(int i=2;i<=n;i++)
if(ww[i]!=ww[i-1]) ww[++top]=ww[i];
for(int i=1;i<=n;i++)
w[i]=lower_bound(ww+1,ww+top+1,w[i])-ww;//离散化
printf("%d\n",dfs(1,n,0,0));
return 0;
}
T2.字符合并
有一个长度为\(n\)的\(01\)串,你可以每次将相邻的\(k\)个字符合并,得到一个新的字符并获得一定分数。
得到的新字符和分数由这\(k\)个字符确定。
你需要求出你能获得的最大分数。
#include <bits/stdc++.h>
using namespace std;
#define N 310
#define mo 998244353
#define int long long
inline int rf()
{
int r;int s=0,c;
for(;!isdigit(c=getchar());s=c);
for(r=c^48;isdigit(c=getchar());
(r*=10)+=c^48);
return s^45?r:-r;
}
int n,k,INF;
int c[257],w[257];
int a[N];
int f[N][N][257];
inline void up(int &x,int y){x=x>y?x:y;}
signed main()
{
n=rf(),k=rf();
for(int i=1;i<=n;i++)
a[i]=rf();
for(int i=0;i<(1<<k);i++)
c[i]=rf(),w[i]=rf();
memset(f,128,sizeof(f)),INF=f[0][0][0];
for(int i=1;i<=n;i++)
f[i][i][a[i]]=0;
for(int l=2;l<=n;l++)
for(int i=1;i<=n-l+1;i++)
{
int j=i+l-1,len=j-i;
while(len>=k) len-=k-1;
for(int m=j;m>i;m-=k-1)
for(int S=0;S<(1<<len);S++)
if(f[i][m-1][S]!=INF)
{
if(f[m][j][0]!=INF) up(f[i][j][S<<1],f[i][m-1][S]+f[m][j][0]);
if(f[m][j][1]!=INF) up(f[i][j][S<<1|1],f[i][m-1][S]+f[m][j][1]);
}
if(len==k-1)
{
int g[2];g[0]=g[1]=INF;
for(int S=0;S<(1<<k);S++)
if(f[i][j][S]!=INF)
g[c[S]]=max(g[c[S]],f[i][j][S]+w[S]);
f[i][j][0]=g[0];f[i][j][1]=g[1];
}
}
int ans=0;
for(int i=0;i<(1<<k);i++)
ans=max(ans,f[1][n][i]);
printf("%lld",ans);
return 0;
}