[EER1]代价

题目背景

个人的遭遇,命运的多舛都使我被迫成熟,这一切的代价都当是日后活下去的力量。 —— 三毛
小 Z 喜欢玩数字游戏。 题目描述 给出一个长度为 n+2 的序列 a i a_i ai ​ ,其中第 1 个数和第 n+2 个数固定为 1。你每次可以选择序列中间的一个数删除(不能是第一个和最后一个),删除位置 p 上的数的代价为 a p − 1 × a p × a p + 1 a_{p-1} \times a_p \times a_{p+1} ap1×ap×ap+1 ​ 。你需要执行这个操作直到无法操作为止。求最小的代价和。

输入格式

第一行一个正整数 n n n。 第二行 n n n 个正整数,第 i i i 个数表示 a i + 1 a_{i+1} ai+1 ​ 。

输出格式

一行一个正整数,表示最小的代价和。

输入输出样例
输入 #1 复制

3 1 2 3

输出 #1 复制

9

输入 #2 复制

4 19 26 8 17

输出 #2 复制

846

输入 #3 复制

6 1 1 1 1 1 1

输出 #3 复制

6

说明/提示

本题采用捆绑测试。
对于 100 % 100\% 100% 的测试点: 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1n106
本题共 6 个子任务,各子任务的分值及约定如下:
子任务 1(1分): a i = 1 a_i = 1 ai=1
子任务 2(14 分): 1 ≤ n ≤ 10 1 \leq n \leq 10 1n10
子任务 3(5 分): 1 ≤ a i ≤ 2 1 \leq a_i \leq 2 1ai2
子任务 4(14 分): 1 ≤ n ≤ 40 1 \leq n \leq 40 1n40
子任务 5(26 分): 1 ≤ n ≤ 500 1 \leq n \leq 500 1n500
子任务 6(40 分):无特殊限制。
特别感谢
idea:smrsky
solu:CYJian
data:iostream

题解

首先发现 a i a_i ai都是正的,然后观察样例发现,所有的样例的删除顺序都是先删两边的数再删中间的,这种删除法导致先删左边和先删右边最后结果都是一样的,唯一不同的是最后一个删的数,所以第一个想法是枚举最后一个删除的,具体实现可以利用前缀和优化成O(N)的:

#include<bits/stdc++.h>
using namespace std;
namespace fastio{
	template<typename tn> void read(tn &a){
	    tn x=0,f=1;char c=' ';
	    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	    for(;isdigit(c);c=getchar() ) x=x*10+c-'0';
	    a=x*f;
	}
	template<typename tn> void print(tn a){
	    if(a<0) putchar('-'),a=-a;
	    if(a>9) print(a/10);
	    putchar(a%10+'0');
	}
};
using namespace fastio;
const int N=1e6+5;
long long f[N],g[N],a[N],n,ans=LONG_LONG_MAX;
int main(){
	read(n);
	a[0]=a[n+1]=1;
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++) f[i]=f[i-1]+a[i]*a[i+1];
	for(int i=n;i>=1;i--) g[i]=g[i+1]+a[i]*a[i-1];
	for(int i=1;i<=n;i++) ans=min(ans,f[i-1]+g[i+1]+a[i]);
	printf("%d\n",ans);
	return 0;
}

这个就是我考场1分代码。。。(话说数据出的很紧啊)

为什么错了呢?原因是之所以先删两边的数再删中间的,是因为 a 1 a_1 a1 a n + 2 a_{n+2} an+2为1,所以如果 a 2 a_2 a2 a n + 1 a_{n+1} an+1也含有1的话就会影响这个贪心的最优性。

如果 a 2 a_2 a2 a n + 1 a_{n+1} an+1也含有1的话的最优应该是按着1分段,然后再做一遍考场1分代码就好了:

#include<bits/stdc++.h>
using namespace std;
namespace fastio{
	template<typename tn> void read(tn &a){
	    tn x=0,f=1;char c=' ';
	    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	    for(;isdigit(c);c=getchar() ) x=x*10+c-'0';
	    a=x*f;
	}
	template<typename tn> void print(tn a){
	    if(a<0) putchar('-'),a=-a;
	    if(a>9) print(a/10);
	    putchar(a%10+'0');
	}
};
using namespace fastio;
const int N=1e6+5;
#define int long long
int l[N],r[N],a[N],n,cnt=0;
int f[N],g[N],ans;
signed main(){
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	a[0]=a[n+1]=1;
	for(int i=0;i<=n+1;i++)
		if(a[i]==1) r[cnt]=i-1,l[++cnt]=i+1;
	cnt--;
	for(int i=1;i<=n;i++) f[i]=f[i-1]+a[i]*a[i+1];
	for(int j=n;j>=1;j--) g[j]=g[j+1]+a[j]*a[j-1]; 
	for(int h=1;h<=cnt;h++){
		int tot=4e18;
		for(int i=l[h];i<=r[h];i++) tot=min(tot,f[i-1]-f[l[h]-1]+g[i+1]-g[r[h]+1]+a[i]);
		if(l[h]<=r[h]) ans+=tot;
	}
	ans+=cnt;
	printf("%lld\n",ans-1);
	return 0;
}

具体需要注意的是要开long long

教训(注意不是收获):

不要轻易放弃,本来是可以写出来贪心的,结果最后丧失梦想去写了55分的DP,还没开long long导致只有28分,实在是得不偿失。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值