首页 > 其他分享 >寒假训练2024/1/26

寒假训练2024/1/26

时间:2024-01-26 23:16:52浏览次数:20  
标签:pre 26 vector dp2 int long 2024 寒假 dp

2024,1,26

今天做石子合并的题比较多

贴一个模板

	for (int len = 2; len <= n; len++) {
		for (int i = 1, j; (j = i + len - 1) <= n; i++) {
            for (int k = i; k < j; k++) {
                if(dp[i][j] > dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1]) {
                    dp[i][j] = dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1];
                }
            }
		}
	}

uva10954

题意:

把n个数相加,把中间所得的结果相加,求这个结果的最小值。

思路:

这题一眼看上去是模拟,但是仔细一想,是个小根堆。

代码:

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

#define int long long

void solve(int n) {
	
	priority_queue<int, vector<int>, greater<int>> q;
	for (int i = 0, t; i < n; i++) {
		cin >> t;
		q.push(t);
	}

	int res = 0;
	while(q.size() > 1) {
		int a = q.top();
		q.pop();
		int b = q.top();
		q.pop();
		q.push(a + b);
		res += a + b;
	}
		
	cout << res << endl;

}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	while(1) {
		int n;
		cin >> n;

		if(n) {
			solve(n);
		}
		else {
			break;
		}
	}

	return 0;
}

uva10003

题意:

把线段切开,让代价最小。

思路:

起初我是把这个题和上面的那个题联系起来,以为就是逆向做,把小线段合并成一个大线段就行,但是不行。

想了很久,才想明白,上面那个题没有要求哪两个个数可以相加,但是对于这个题来说切线段和组合线段是相对的,所以必须是相邻的线段组合。

我看了题解是四边形不等式优化dp 类似的洛谷题是石子合并,当然有两个弱化版本,可以用三位循环dp求解,我先做这两个题然后学优化,最后来做这个题。

洛谷P1775 石子合并(弱化版)

题意:

N(N≤300)要将 N 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。试找出一种合理的方法,使总的代价最小,并输出最小代价。

思路:

N很小,考虑三重循环dp。

但是有一点小曲折:前缀和还是很好想的,通过前缀和进行区间的寻找,然后dp得到了下面代码:

for (int i = 1; i < n; i++) {
		for (int j = i + 1; j <= n; j++) {
			for (int k = i; k < j; k++) {
				if(dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1] < dp[i][j]) {
					dp[i][j] = dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1];
				}
			}
 		}
	}

看起来没有问题,但是得不出样例,看了题解之后才明白,要从小区间开始dp

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

#define int long long

void solve() {
	int n;
	cin >> n;

	vector<int>pre(n + 1);
	vector dp(n + 1, vector<int>(n + 1));
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			dp[i][j] = 1e18;
		}
	}
	for (int i = 1; i <= n; i++) {
		cin >> pre[i];
		pre[i] += pre[i - 1];
		dp[i][i] = 0;
	}
	
	for (int len = 2; len <= n; len++) {
		for (int i = 1; i + len - 1 <= n; i++) {
			int j = i + len - 1;
				for (int k = i; k < j; k++) {
					if(dp[i][j] > dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1]) {
						dp[i][j] = dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1];
					}
				}
		}
	}
	
	cout << dp[1][n] << endl;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int T = 1;

	while(T--) {
		solve();
	}

	return 0;
}

这个dp的实质就是大区间枚举拆分成两个小区间,通过遍历分割点找到最优解。

P1880 [NOI1995] 石子合并

(样例想了半天)这个题和上个石子合并的差别就在于把一段变成了环形,环形的话要考虑第一个和最后一个是相邻的,我按照套路开了两倍大小三维循环dp AC了。

但是比较曲折(可能好久不写题了),初始化的部分调了很久。

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

#define int long long

void solve() {
	int n;
	cin >> n;

	vector<int>pre(2 * n + 1);
	vector dp(2 * n + 1, vector<int>(2 * n + 1));
	vector dp2(2 * n + 1, vector<int>(2 * n + 1));
	for (int i = 1; i <= 2 * n; i++) {
		for (int j = 1; j <= 2 * n; j++) {
			dp[i][j] = 1e18;
			dp2[i][j] = -1;
		}
	}
	for (int i = 1; i <= n; i++) {
		cin >> pre[i];
		pre[i + n] = pre[i];
		pre[i] += pre[i - 1];
	}
	for (int i = 1; i <= 2 * n; i++) {
		dp[i][i] = 0;
		dp2[i][i] = 0;
	}
		
	for (int i = n + 1; i <= 2 * n; i++) {
		pre[i] += pre[i - 1];
	}

	for (int len = 2; len <= n; len++) {
		for (int i = 1; i + len - 1 <= 2 * n; i++) {
			int j = i + len - 1;
				for (int k = i; k < j; k++) {
					if(dp[i][j] > dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1]) {
						dp[i][j] = dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1];
					}
					if(dp2[i][j] < dp2[i][k] + dp2[k + 1][j] + pre[j] - pre[i - 1]) {
						dp2[i][j] = dp2[i][k] + dp2[k + 1][j] + pre[j] - pre[i - 1];
					}
				}
		}
	}
	int minm = 1e18, maxm = -1;
	for (int i = 1; i <= n + 1; i++) {
		minm = min(minm, dp[i][i + n - 1]);
		maxm = max(maxm, dp2[i][i + n - 1]);
	}

	cout << minm << endl << maxm << endl;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);

	int T = 1;
	// cin >> T;

	while(T--) {
		solve();
	}

	return 0;
}

uva10003

做了上面两个题我发现其实不需要优化,这个题的复杂度$n\le50$ 就算是三重循环的区间dp也可以解决。

注意格式

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

#define int long long

void solve(int len) {
	int n;
	cin >> n;

	vector<int> v(n);
	for (int i = 0; i < n; i++) {
		cin >> v[i];
	}

	int la = 0;
	vector<int>vv;
	for (auto it : v) {
		vv.push_back(it - la);
		la = it;
	}
	vv.push_back(len - la);

	vector<int>pre(n + 2);
	for (int i = 0; i < n + 1; i++) {
		pre[i + 1] = pre[i] + vv[i];
	}
	
	n++;
	vector<vector<int>> dp(n + 1, vector<int>(n + 1));
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if(i != j) {
				dp[i][j] = 1e18;
			}
			else {
				dp[i][j] = 0;
			}
		}
	}
	
	for (int len = 2; len <= n; len++) {
		for (int i = 1, j; (j = i + len - 1) <= n; i++) {
			for (int k = i; k < j; k++) {
				if(dp[i][j] > dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1]) {
					dp[i][j] = dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1];
				}
			}
		}
	
	}

	cout << "The minimum cutting is " << dp[1][n] << ".\n";
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	while(1) {
		int len;
		cin >> len;

		// cerr << len << "**\n";
		if(len) {
			solve(len);
		}
		else {
			break;
		}
	}

	return 0;
}

标签:pre,26,vector,dp2,int,long,2024,寒假,dp
From: https://www.cnblogs.com/yyc4591/p/17990566

相关文章

  • 2024 笔记类软件对比(非常主观)
    在线的离线的自用之后的体验主要关注易用性,持久性,价格敏感Logseq:软件在github上开源,文档都是本地的markdown文件形式。工作上用它记了差不多一年,双向链接功能很好用。card和画板功能我没怎么用。因为同步不方便/记录需要先组织好逻辑,逐渐放弃Notion:白嫖了一个教育plan,不......
  • 游记 PKUWC 2024
    2024北京大学全国优秀中学生信息学冬季体验营1.25~1.27重庆市育才中学校1.2613:00网络卡顿挂了5分钟。但没事,因为在写ntt板子。还写挂了。然后看了题目,T1应该可以做出来,T2被吓到了,T3可以想一下。T1->T2->T3。T1首先写了个区间dp交上去。目测是一个dp优化,把......
  • THUWC2024游记
    RP++Day1T1看了一会儿居然没思路。看到数据范围\(n\le15\)想到可以状压,但怎么也想不出来,只好先打掉\(m=16\)和\(n=4\)两档暴力。然后脑子好一点了,枚举前\(i\)个人确定了集合\(S\),发现要枚举子集,预处理了一下做到了\(O(3^nm)\),喜提\(77\)。然后脑子锈掉了。这个......
  • 2024.01.25 近期练习
    CF1856E2如果\(n\le5000\)考虑怎么做。首先我们对于每个节点只考虑大小关系,最后只需要从小到大标号即可。我们考虑把答案放到LCA处统计。若其只有两个儿子\(v_1,v_2\),那最多只有\(siz_{v_1}\timessiz_{v_2}\)个会被统计,即令\(v_1\)所有数大于\(v_2\)。若有多个儿......
  • SMU 2024 winter round1
    题目链接A.直接输出#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongconstintN=1e5+10;voidsolve(){cout<<"Goodcodeisitsownbestdocumentation."<<'\n';}signedmain(){ios::sync_with_st......
  • 2024年1月Java项目开发指南15:vue3+AntDesignVue 设计页面
    考虑到有的同学对vue3不熟悉,因此,我把ControlView.vue这个页面清空,我们从0开始写。<templatestyle="width:100%"></template><scriptsetup></script><stylescoped></style>搭建页面的基本框架展开代码后复制你需要的代码。比如我选择上中下这种结构,我就复制上......
  • 期末 whk 游记 && 1.26闲话
    喜报:我B20了政治和物理第20题都选了B,然后都错了哈哈。现在rank260,体育rank1能给我救回来吗,但是体育最高跟别人拉开2分的距离啊,有点慌了。好像集训可以带高级手机,但是能不能集训还得看这次whk成绩,恼了。现在还差两科出啊。语文101.5,但是现代文阅读只扣了三分,前面......
  • 闲话1.26
    想回家想睡觉想放假快魔怔了又写一天题了,周日还你妈有模拟赛,我他妈想睡觉啊。隔壁初中都放寒假了,妈的我们啥时候才能放啊,妈的还有两周啊啊啊啊啊啊啊啊我他妈不想接着在学校呆着了我想回家我想睡觉啊啊啊啊啊啊啊中午洗头把保暖和校服袖子全弄湿了......
  • P2602 数字计数 题解
    QuestionP2602数字计数求\([a,b]\)中的所有整数中,每个数出现的次数Solution数位DP板子题定义\(dp[i]\)表示\(i\)位数的每种数字有多少个,我们把\(0\)和\(00\)看成两种不同的数,并且\(00\)中算\(0\)出现过两次显然,\(0\sim9\)在\(i\)位数中出现的次数是一样......
  • 【随笔】2024年1月1日
    关于Febonacci的一些事学了矩阵加速递推遂顺手给你谷的板子题又过了一遍对于“已知递推式求转移矩阵”的方法仍有疑惑与巨佬WPP交流并丢给WPP一道题请他口糊题:求Febonacci前n项的和(n<=1e18)正解是把S(n)(表示前n项的和)塞到矩阵里一起转移答案矩阵F(n)={f(n-1)f(n-2)S(n......