洛谷P8297 LANCI

【题目描述】

Mirko 在阁楼里发现了 N 个链。每个链由一些节组成,其中每个节最多有两个相邻节。每个节都可以打开或合上,因此可以将链分开或连成更长的链。

Mirko 希望把所有链连成一条巨大的链,并且打开或合上尽可能少的节。

例如,假设 Mirko 只有 3 个链,每个链只有一个节,他可以打开其中一个,并且连上另外两个再合上。

给定链的数量以及每个链的长度,找到 Mirko 必须打开和关闭的最小节数,使它们全部在一个长链上。

输入格式

第一行一个整数 N (2≤N≤5×105),表示链的数量。

第二行 N 个正整数Li​ (1≤Li​≤106),表示第 i 个链的长度。

输出格式

输出仅一行一个整数,表示最少要打开的节数。

输入输出样例

输入 #1

2
3 3

输出 #1

1

输入 #2

3
1 1 1

输出 #2

1

输入 #3

5
4 3 5 7 9

输出 #3

3

 【思路】

每次我们都可以进行打开某个链其中的一个节,求最少的打开次数

首先,显而易见的是,对于任意的一个链来说,如果打开中间的节将毫无意义,只会白白浪费次数。

暴力思想来说的话,我们可以依次打开除最后一个链以外的每一个链的最后一个节,这样我们就一个一定能串起来,但这样并不是最优解。

因为这样花费的次数一定为n-1。

例如,我们不能满足例子3.

我们考虑如果将一个链每一个节(n节)都拆下来,那么这样就能将n+1个链串起来。这样就比开始的暴力思想次数要少。

 注意:对于第一个全部拆下来的链,能将n+2个链串起来,因为包括了被拆的链

 最后,我们先将输入的链进行排序,然后从小到大判断是直接暴力还是全拆。

【代码】

#include<bits/stdc++.h>
using namespace std;
using ll =long long;
const int N=5e5+10;
//最好的情况就是全部拆成1的情况
int main() {
	int n,sum[N];
	cin>>n;
	for(int i=1; i<=n; i++) {
		cin>>sum[i];
	}
    //将sum的数据进行排序
	sort(sum+1,sum+n+1);
	int i=1,ans=0,falg=0;
	while(1) {
		//考虑两个情况
		//如果把最小的全拆了刚好满足或者不满足
		//那么就全拆了
		if(n-sum[i]>=2) {
			if(falg==0) {
				n-=(sum[i]+2);
			} else {
				n-=(sum[i]+1);
			}
			falg=1;
			ans+=sum[i];
			i++;
		} else {
			//中途就满足了
			ans+=n;
			cout<<ans;
			return 0;
		}
		if(n<=0) {
			cout<<ans;
			return 0;
		}
	}
}

如果有更好的方法或者思路,欢迎评论区进行讨论

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值