P2654 原核生物培养
很板的题。
容易发现,每次操作相当于,总是前 小的数删除,然后在原序列里加入一个数,这个操作很好实现,直接考虑把原序列前 个数删掉,然后在最后一个位置加上新的数,然后对整个数组排序即可。
关于加入的这个数,是前 小的数的和,因为剩余最后一个生物质量是他们质量总和,这个显然。
所以说,每次操作是互不影响的,考虑一次操作,一次操作其实就是把这 个数按照一定顺序放置,每次可以删掉相邻两个。代价是他们的和,以及把他们的和放到删掉数的位置上去。
不难想到区间动态规划,考虑设 表示删除了 以后的最小代价,发现只需要枚举中间断点 ,看作是 剩下的生物和 生物残杀,代价就是整个区间的和。
其中 是前缀和。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现