P2654 原核生物培养

Link

很板的题。

容易发现,每次操作相当于,总是前 mm 小的数删除,然后在原序列里加入一个数,这个操作很好实现,直接考虑把原序列前 mm 个数删掉,然后在最后一个位置加上新的数,然后对整个数组排序即可。

关于加入的这个数,是前 mm 小的数的和,因为剩余最后一个生物质量是他们质量总和,这个显然。

所以说,每次操作是互不影响的,考虑一次操作,一次操作其实就是把这 mm 个数按照一定顺序放置,每次可以删掉相邻两个。代价是他们的和,以及把他们的和放到删掉数的位置上去。

不难想到区间动态规划,考虑设 dpi,jdp_{i,j} 表示删除了 [i,j][i,j] 以后的最小代价,发现只需要枚举中间断点 kk,看作是 [i,k][i,k] 剩下的生物和 [k+1,j][k+1,j] 生物残杀,代价就是整个区间的和。

dpi,j=min{dpi,k+dpk+1,j+sjsi1}dp_{i,j}=\min\{dp_{i,k}+dp_{k+1,j}+s_j-s_{i-1}\}

其中 ss 是前缀和。

#include<bits/stdc++.h>
using namespace std;
const int N =1001;
#define int long long 
int n,m,k,sum,a[N],dp[21][21],c[21],s[21],dis[N];
void Add(){
	int node=-1,lst,ans=0;
	for(int i=1;i<=m;i++)	ans+=a[i];
	for(int i=m+1;i<=n;i++)	a[i-m]=a[i];
	n-=m,n++,a[n]=ans;
	sort(a+1,a+n+1);
}
void DP(){
	memset(dp,0x3f,sizeof(dp));
	int ans=dp[0][0];
	for(int i=1;i<=m;i++)	c[i]=c[i+m]=a[dis[i]];
	for(int i=1;i<=2*m;i++)	s[i]=s[i-1]+c[i],dp[i][i]=0;
	for(int len=2;len<=m;len++)
		for(int i=1;i<=2*m-len+1;i++){
			int j=(i+len-1);
			for(int k=i;k<j;k++)	
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]);
		}
	for(int i=1;i<=m;i++)	ans=min(ans,dp[i][i+m-1]);
	sum+=ans;
}
signed main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)	cin>>a[i];
	sort(a+1,a+n+1);
	while(k--){
		for(int i=1;i<=m;i++)	cin>>dis[i];
		DP(),Add();
	}
	cout<<sum<<endl;
	return 0;
}
posted @   June_Failure  阅读(2)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示