老夫聊发少年狂,碱金属,丢池塘。浮溶游响,激起千朵浪。氢|

Yaosicheng124

园龄:1年3个月粉丝:8关注:11

CF 1579 G

题目描述

在一根数轴上,你将依次放入 N 根长度为 di 的线段。

每次,你可以将线段放置于数轴上并使得其中一段等于上一段的末尾。假设上一次的末尾为 x,则这次你可以将线段置于 [xd,x][x,x+d],并将 x 设为 xdx+d

求最终摆出的的·线段长度并最小值。

思路

dpi,j 表示考虑前 i 根线段,上一次的末尾离左端点的距离为 j 时离右端点最近可以是多少。

很明显有转移:

dpi+1,j+di+1max(0,dpi,jdi+1)dpi+1,max(0,jdi+1)dpi,j+di+1

但这里的 j 有多大呢?

你可能会想到 1000,毕竟最大线段长度就 1000

但如果当前是右端点,接着依次出现一个 500,1000,此时就无法做到不超过右端点,即 j>1000

但实际上 j2000,因为只要有一个长度为 2000 的区间,在里面怎么跳都是不会越界的。

时空复杂度均为 O(Nmax{di})

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 10001, MAXV = 2001;

int t, n, a[MAXN], dp[MAXN][MAXV], ans;

void Solve() {
  cin >> n;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
  }
  for(int i = 0; i <= n; ++i) {
    for(int j = 0; j <= 2000; ++j) {
      dp[i][j] = 114514;
    }
  }
  dp[0][0] = 0;
  ans = 114514;
  for(int i = 0; i < n; ++i) {
    for(int j = 0; j <= 2000; ++j) {
      if(j + a[i + 1] <= 2000) {
        dp[i + 1][j + a[i + 1]] = min(dp[i + 1][j + a[i + 1]], max(0, dp[i][j] - a[i + 1]));
      }
      dp[i + 1][max(0, j - a[i + 1])] = min(dp[i + 1][max(0, j - a[i + 1])], dp[i][j] + a[i + 1]);
    }
  }
  for(int i = 0; i <= 2000; ++i) {
    ans = min(ans, dp[n][i] + i);
  }
  cout << ans << "\n";
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  for(cin >> t; t--; Solve()) {
  }
  return 0;
}

本文作者:Yaosicheng124

本文链接:https://www.cnblogs.com/yaosicheng124/p/18405536

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Yaosicheng124  阅读(7)  评论(0编辑  收藏  举报
  1. 1 Minecraft C418
Minecraft - C418
00:00 / 00:00
An audio error has occurred.

暂无歌词

加载中…

{{tag.name}}

{{tran.text}}{{tran.sub}}
无对应文字
有可能是
{{input}}
尚未录入,我来提交对应文字
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示