【USACO题库】 动态规划 汇总(普及-/普及)

【攻略】USACO 比赛指导建议和常见问题 USACO 比赛指导建议和常见问题 阅读详情

数据结构提高是够用了=-=虽然树状数组没学 但是其他类问题只能打到普及

普及啊啊啊!!!

而且这边省选组都是什么仙人掌啊,什么系什么点对啊...感觉数据结构并没有什么用 (实际上很有用但我不会用就是了)

然后颓到提高组来了..结果全是模拟还有一堆没学的其他玩意 (回文自动机) 数据结构也没考几次..还是线段树用来辅助搜索......

看看普及 全是水题 当然偶尔还有一点我不会的 (某些算法) =-= 万念俱灰 无奈之下待在了提高组...

于是在老师的强迫我坚毅的信念下开始填坑=-=这里是dp专题

 找找常见dp的类型......普通dp 树形dp 区间dp 状压dp 数位dp 插头dp......一个一个补

废话不多说先放题目

---------------------------------------第二部分---------------------------------------

2.2.2 Subset Sums集合

题目描述

对于从1到N(1 <= n <= 39)的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的。

举个栗子,如果N=3,对于{1,2,3}能划分成两个子集合,他们每个的所有数字和是相等的:

  • {3} and {1,2}

这是唯一一种分法(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数)

如果N=7,有四种方法能划分集合{1,2,3,4,5,6,7},每一种分发的子集合各数字和是相等的:

  • {1,6,7} and {2,3,4,5} {注 1+6+7=2+3+4+5}
  • {2,5,7} and {1,3,4,6}
  • {3,4,7} and {1,2,5,6}
  • {1,2,4,7} and {3,5,6}

给出N,你的程序应该输出划分方案总数,如果不存在这样的划分方案,则输出0。程序不能预存结果直接输出。

 

时空范围呢 是1000ms/256MB

 

INPUT FORMAT

输入文件只有一行,且只有一个整数N

SAMPLE INPUT

7

 

OUTPUT FORMAT

输出划分方案总数,如果不存在则输出0。

SAMPLE OUTPUT

4

先来思考一下=-= n范围是1~39 那么死磕算法肯定是不行的 O(2的n次方)

然后仔细分析题目 每个数字必定属于一个集合 要不是这个要不是那个 并且两集合相等 其总和为(n + 1) * n (好像是高斯那啥)

除2即得单个集合的值 然后这集合里面的数是1~n 可选可不选

不就是01背包么=w=

那么对于每一个数 我们可选 也可不选

设dp[a][b]为 取前a个数 总和为b的方案数量 ...等等 为什么这么设??

动态规划 我们从小找起 一开始只有一个数 可能的值为 1 或 0

然后第二个数 在这两个值上相加...以此类推

那么a就是一个一个数到n 然后b呢 因为按上面那种推法可能结果很多 但显而易见可能结果绝对小于数的总和

因此以数的总和作为b...好了 初始化第一个数 然后开始推

之后呢 看代码注释吧~

下放代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#define ll long long
using namespace std;
ll dp[5 << 4][5 << 7];//坑啊 上限大于2147483647 然后我AC率掉了一截
int main()
{
	int n;
	scanf("%d",&n);//认真仔细地输入
	int all = (n + n * n) >> 1;//这里原本是(n + 1) * n / 2 但为了我高大上的位运算=-= 式子怎么来的不说了 别人高斯小学就发现了
	if (all % 2) {putchar('0'),putchar('\n'); return 0;}//判断总数如果为单数 绝对不可能平分成两整数 直接退出 皆大欢喜
	all = all >> 1;//先除以2 因为我发现总数后面用不到
	dp[1][1] = 1;//预处理1个数的情况
	dp[1][0] = 1;//同上
		for (int a = 2 ; a <= n ; a ++)//判断加上第a个数的情况
			for (int b = 0 ; b <= all ; b ++)//判断原有所有状态 更新加上/不加上a的状态
			if (b < a) dp[a][b] = dp[a - 1][b];//更新不加a达到的数的和的方案数
				  else dp[a][b] = dp[a - 1][b] + dp[a - 1][b - a];//更新加上a达到的数的和的方案数
	printf("%d\n",dp[n][all] / 2);//因为这里两个子集一并求了因此要除2... 
	return 0;
}

 

2.3.2 Cow Pedigrees奶牛家谱

题目描述

农民约翰准备购买一群新奶牛。 在这个新的奶牛群中, 每一个母亲奶牛都生两小奶牛。这些奶牛间的关系可以用二叉树来表示。这些二叉树总共有N个节点(3 <= N < 200)。这些二叉树有如下性质:

  1. 每一个节点的度是0或2。度是这个节点的孩子的数目。
     
  2. 树的高度等于K(1 < K < 100)。高度是从根到任何叶子的最长的路径上的节点的数目; 叶子是指没有孩子的节点。

有多少不同的家谱结构? 如果一个家谱的树结构不同于另一个的, 那么这两个家谱就是不同的。输出可能的家谱树的个数除以9901的余数。

INPUT FORMAT

第1行: 两个空格分开的整数, N和K。

SAMPLE INPUT

5 3

 

OUTPUT FORMAT

第 1 行: 一个整数,表示可能的家谱树的个数除以9901的余数。

SAMPLE OUTPUT

2

OUTPUT DETAILS:

有5个节点,高为3的两个不同的家谱:

      @                  @
 
      / \                   / \
   @  @    和    @   @
   / \                         / \
@ @                    @ @

然后别的博客上有个人解释和USACO的官方解释 这里给个传送门 Tip:还是回来吧 我这里详细得多OvO

分析一下嘛 这题呢

就是求 一定高度 树上有一定节点 分布在树上的方法

然后方法有两种: 子树没节点 或 子树两个节点 然后后者子树下一层的节点也同这两种

设子树为2个 是 X 为0个 是 O 则

该层某地方的上层 可能是XOOXXOXXXOXOOXX.........嘛 太复杂了 我们排除掉纯暴力的想法 然后......DP!

树形DP!!!

从根节点开始推 首先我们知道 根节点只有一种情况——有 因此他肯定有两儿子

嘛 后面的点嘛 就有两种情况 有或没有 后面每层都这样

然后如果要到下一层 那下一层的点肯定有父亲 但他父亲那层的点有多少...我们只知道这种情况下 只有他父亲必然存在

叶子节点必然有父亲

那点就是不确定的了......那我们就要推啊 好 我们设点~ (喂喂怎么能这么爽快我还不清楚情况呢)

题目正好又给了节点个数 3≤N<200 那我们从3开始推 根据节点度数很轻松地能发现 总共点数只可能是奇数 刚好 1 + 2 = 3

那么从3推起刚好能衔接上!!

Tip:如果想读入N时预处理去掉偶数情况也可以 但是我懒...反正后面推导后这些情况的答案也是0

嘛 我们点数22累加 算到N就好

嗯 点可以推到N 那深度呢......好 我们设深度~ (喂喂怎么又是这样子解释清楚啊)

呐 我们要求深度为K 点数为N的情况嘛 然后一开始的情况已经知道了 深度为1 点数为1 情况为1

那就推深度和情况啊 (搞了这么久原来是这个意思...)

嘛 我们深度11累加 算到K就好

这里考虑点数一定时 分布在所有深度的情况

因此这里存的状态是当前节点数 当前深度及以上(浅) 的 所有状态

点数一定 点数分布太多太杂 像上面的XOOXXOXXXOXOOXX.........我们肯定不能对当前节点所在的状态进行推导 动态规划嘛

说了点数一定 那就按当前点数推 点数分布...树叉太多了 怎么办?

只取根节点的左右子树!! 而且根据n的范围 这样刚好可以涵盖所有情况 当然 如果 n ≥ 9 理论推测你可以取四棵树

那就设左子树节点...根节点 有 1个左 1个右 然后后面其他节点就只可能0/2

那初始状态为1左(因为没有左自然没有右 根节点没有左边那一个就没有右边那一个)

设当前z总节点个数为a 左节点为cnt(count缩写)个 右节点就有a - cnt - 1 个(1个根节点)

那显而易见 左边 cnt 个 右边 a - cnt - 1 个的情况乘起来 就是总共a个的情况

原因 例如左边 XOOOOO,OXOOOO....很多情况 则右边OXXXXX,XOXXXX...... 总结点相同

但对于左边每一种排列 右边都有很多种不一样的排列 只要节点总和相同 位置可以任意换 然后对于右边也是 因此要乘 不是加!!

简单理解一下: 两个数列 长度均为6 每个数都不同 问你可能取到多少种不同的数的组合......6×6啊 多明显......

然后对于每一种之前的情况 推到下一层只可能有一种 那就是这层某点有两儿子 那么就可以推出下层a个节点的情况——

         dp[a][dep] += dp[cnt][dep - 1] * dp[a - cnt - 1][dep - 1] 下行有对应的解释=-=

该层a个点的情况||上层左边cnt个点的情况||上层右边a-cnt-1个点的情况

此处为什么+=??

其实因为1个点的时候没办法找式子(根上面还有什么呢) 我们初始化了一下=-=

for (int a = 1 ; a <= h ; a ++) dp[1][a] = 1;(别问我高度为什么不用k要用h......)

这里的1 就是推不到下一层的时候的情况——0

dp[a][dep]只会更新一次(看循环就知道了) 此处加的就是以上情况

说白了就是+1...

但for循环初始化是必须的 因为循环如果搜到了上面情况 0 × 0 = 0 再 + 0 = 0 那根本推不下去

这也是初始化之一~~

因此我们可以推到N个点 深度K的情况 此处注意——

求的是深度为K及比他浅的所有情况和

因此要减去深度比K浅的所有情况 [k - 1] 正好包括了那些所有情况

最后还有一个坑点 关于取膜♂取mod的事——

上层取模后的数可能比这层的大!!! 然而并不会大于9901

因此我懒得判情况 直接加上9901 和原来的数一起 然后取模即可

下放代码~

#include <iostream>
#include <cstdio>
#define mod 9901
using namespace std;
int dp[1 << 9][1 << 10];
int main() {
	int n,h;
	scanf("%d%d",&n,&h);
	for (int a = 1 ; a <= h ; a ++) dp[1][a] = 1;//预处理 1个点时所有深度的情况(只可能在深度1有1个点)
	for (int a = 3 ; a <= n ; a += 2)//总节点数循环
	for (int dep = 2 ; dep <= h ; dep ++)//每个节点的深度推导
	for (int cnt = 1 ; cnt < a ; cnt += 2) {//总结点相同时的不同情况 这里左右并不是遍历到终点 因为左右子树互换也算一种情况..
		dp[a][dep] += dp[cnt][dep - 1] * dp[a - cnt - 1][dep - 1];//精华の更新
		dp[a][dep] %= mod;//取模
	}
	printf("%d\n",(dp[n][h] + mod - dp[n][h - 1]) % mod);//坑人的输出
	return 0;
}

 

2.3.4 Money Systems货币系统

题目描述

母牛们不但创建了他们自己的政府而且选择了建立了自己的货币系统(不愧于牛批这个词)。

[In their own rebellious way],他们对货币的数值感到好奇。

传统地,一个货币系统是由1,5,10,20 或 25,50, 和 100的单位面值组成的。

母牛想知道有多少种不同的方法来用货币系统中的货币来构造一个确定的数值。

举例来说, 使用一个货币系统 {1,2,5,10,...}产生 18单位面值的一些可能的方法是:18x1, 9x2, 8x2+2x1, 3x5+2+1,等等其它。

写一个程序来计算有多少种方法用给定的货币系统来构造一定数量的面值。

保证总数将会适合long long (C/C++) 和 Int64 (Free Pascal)。

 

INPUT FORMAT

货币系统中货币的种类数目是 V 。 (1<= V<=25)

要构造的数量钱是 N 。 (1<= N<=10,000)

第 1 行:二整数, V 和 N
第 2 ..V+1行:可用的货币 V 个整数 (每行一个 每行没有其它的数)。

SAMPLE INPUT

3 10
1 2 5

 

OUTPUT FORMAT

单独的一行包含那个可能的构造的方案数。

SAMPLE OUTPUT

10

炒鸡经典的啦=-=

感觉很像多重背包 就一个数可以取多次 然后和其他数(或者不和)组合 输出能达到指定value的情况

不对 就是多重背包=-=

那我们考虑每次遍历一个"物品"的价值 因为怕更新到负数的地方 因此从物品价值开始更新

直到指定value 然后更新过程中每次能达到的value

即如果一个value可以通过以前的一个value加上物品价值得到 当前value就加上之前那个value的情况

此处背包存的是到达某价值的情况数量啊 切记=-=

通过把每个物品遍历一次 就可以得到所有的情况了

查询O(1) 然而这种题目怎么可能给你查询多次2333

只用输出第value个就好~(忽略0下标)

注意此处可能结果大于int=-= 用 long long 存......能尽量不用还是不用好 毕竟慢死

下放代码~

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
int v[1 << 5];
ll ans[5 << 11] = {1};
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for (int a = 1 ; a <= n ; a ++) scanf("%d",&v[a]);
	for (int a = 1 ; a <= n ; a ++)
		for (int b = v[a] ; b <= m ; b ++)
			ans[b] += ans[b - v[a]];
	printf("%lld\n",ans[m]);
	return 0;
}

 

 

 

---------------------------------------第三部分---------------------------------------

3.1.2 Score Inflation总分

题目描述

学生在我们USACO的竞赛中的得分越多我们越高兴(这就是你出水题的理由??)。

我们试着设计我们的竞赛以便人们能尽可能的多得分,这需要你的帮助。

我们可以从几个种类中选取竞赛的题目,这里的一个"种类"是指一个竞赛题目的集合,解决集合中的题目需要相同多的时间并且能得到相同的分数。

你的任务是写一个程序来告诉USACO的职员,应该从每一个种类中选取多少题目,使得解决题目的总耗时在竞赛规定的时间里并且总分最大。

输入包括竞赛的时间,M(1 <= M <= 10,000)(不要担心,你要到了训练营中才会有长时间的比赛)和N,"种类"的数目1 <= N <= 10,000。

后面的每一行将包括两个整数来描述一个"种类":

第一个整数说明解决这种题目能得的分数(1 <= points <= 10000),第二整数说明解决这种题目所需的时间(1 <= minutes <= 10000)。

你的程序应该确定我们应该从每个"种类"中选多少道题目使得能在竞赛的时间中得到最大的分数。

来自任意的"种类"的题目数目可能任何非负数(0或更多)。

计算可能得到的最大分数。

 

INPUT FORMAT

第 1 行:M, N--竞赛的时间和题目"种类"的数目。
第 2-N+1 行:两个整数:每个"种类"题目的分数和耗时。

SAMPLE INPUT

300 4
100 60
250 120
120 100
35 20

 

OUTPUT FORMAT

单独的一行包括那个在给定的限制里可能得到的最大的分数。

SAMPLE OUTPUT

605
{从第2个"种类"中选两题第4个"种类"中选三题}

和上题一样 多重背包

这里存的是到达一个容量时的最大价值

懒得优化的我背包后直接搜了=-=下放代码(最大点316ms真是羞耻)

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int ans[5 << 11],w[5 << 11],v[5 << 11];
int main()
{
	int time,m,tot = 0;
	scanf("%d%d",&time,&m);
	for (int a = 1 ; a <= m ; a ++) scanf("%d%d",&v[a],&w[a]);
	for (int a = 1 ; a <= m ; a ++)
		for (int b = w[a] ; b <= time ; b ++)
		ans[b] = max(ans[b],ans[b - w[a]] + v[a]);
	for (int a = 10000 ; a > 0 ; a --)
	if (ans[a]) tot = max(ans[a],tot);
	printf("%d\n",tot);
	return 0;
}

 

3.1.6 Stamps邮票

题目描述

已知一个 N 枚邮票的面值集合(如,{1 分,3 分})和一个上限 K —— 表示信封上能够贴 K 张邮票。计算从 1 到 M 的最大连续可贴出的邮资。

 

例如,假设有 1 分和 3 分的邮票;你最多可以贴 5 张邮票。很容易贴出 1 到 5 分的邮资(用 1 分邮票贴就行了),接下来的邮资也不难:

  • 6 = 3 + 3
  • 7 = 3 + 3 + 1
  • 8 = 3 + 3 + 1 + 1
  • 9 = 3 + 3 + 3
  • 10 = 3 + 3 + 3 + 1
  • 11 = 3 + 3 + 3 + 1 + 1
  • 12 = 3 + 3 + 3 + 3
  • 13 = 3 + 3 + 3 + 3 + 1。

然而,使用 5 枚 1 分或者 3 分的邮票根本不可能贴出 14 分的邮资。因此,对于这两种邮票的集合和上限 K=5,答案是 M=13。

 

INPUT FORMAT

第 1 行:两个整数,K 和 N。K(1 <= K <= 200)是可用的邮票总数。N(1 <= N <= 50)是邮票面值的数量。
第 2 行 .. 文件末:N 个整数,每行 15 个,列出所有的 N 个邮票的面值,面值不超过 10000。

SAMPLE INPUT

5 2

1 3

 

OUTPUT FORMAT

 

第 1 行:

一个整数,从 1 分开始连续的可用集合中不多于 K 张邮票贴出的邮资数。

SAMPLE OUTPUT

13

 

一看就知道是动规了 感觉也很像......背包??

这里求的是可能得到的数的集合

如果一个个列举肯定会爆 最大值两百万 你还要枚举每个组合 还有好多重复的......

感觉像背包 实际上......应该是吧 给了总的容量 然后自己搭配

因为此处求的是组合个数 就是可能的价值个数 dp数组怎么能存容量呢 

于是dp数组存价值 然后价值一样的时候 容量就可能不一样了

dp是找最优嘛 那么相同价值 容量越小越好 不是吗

那我们因此dp数组初始赋极大值(好吧两百也行) 然后价值为0时容量为0

枚举每个价值 能否通过之前的某种状态 加上这个价值得到当前状态

然后当前状态容量与更新后的比较 如果更小就更新 因为可以装更多 岂不美哉~~

如果装到一个地方容量大于指定的了 说明大包溢出了 直接退出~

(一开始我以为中间可能有断层还开了tot一个个记 结果发现并没有必要 全是连着的啊啊啊)

大概思路就这样 下放代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = 2000010;
int ans[1 << 21],v[1 << 6],tot;
int main()
{
	int n,m;
	memset(ans,0x7f,sizeof(ans));
	ans[0] = 0;
	scanf("%d%d",&n,&m);
		for (int a = 1 ; a <= m ; ++ a) scanf("%d",&v[a]);
		for (int a = 1 ; a <= MAX ; ++ a)
		{
			for (int b = 1 ; b <= m ; b ++)
				if (a - v[b] < 0) continue; else
				ans[a] = min(ans[a],ans[a - v[b]] + 1);
			if (ans[a] > n)
			{
				printf("%d\n",a - 1);
				break;
			}
		}
	return 0;
}

 

3.2.2 Stringsobits__01串

题目描述

考虑排好序的N(N<=31)位二进制数。

 

你会发现,这很有趣。因为他们是排列好的,而且包含所有可能的长度为N且含有1的个数小于等于L(L<=N)的数。

 

你的任务是输出第I(1<=I<=长度为N的二进制数的个数)大的,长度为N,且含有1的个数小于等于L的那个二进制数。

 

INPUT FORMAT

共一行,用空格分开的三个整数N,L,I。

SAMPLE INPUT

5  3  19

 

OUTPUT FORMAT

共一行,输出满足条件的第I大的二进制数。

SAMPLE OUTPUT

10011

[特典]样例解释---->满足条件的二进制数有(从小到大排列)——

0,1,10,11,100,101,110,111,1000,1001,1010,1011,1100,1101,1110,10000,10001,10010,10011

0也是啊 发现没有 发现没有!!!

 

我天=-=这个动规我愣是没想出来......

好了正题...这题给了你位数 要你求这个位数的所有位数的数的集合里 从小到大排第 i 个 且 含有 1 的数量小于等于 L 的数

分析 每个位数只有两个状态 0 和 1 从第一位开始状态可初始化 我们考虑一维给位数

然后题目要求含有 1 的数量 考虑存不同状态时 1 的 个数 那二维就给他了

状态怎么转移??

首先头肯定是 1 的了 不然......就怪怪的 对 就怪怪的

但是本题动规就是按照有头为 0 来考虑的!!! 怪怪的 对 怪怪的 那为什么呢

因为头为 0 的时候 就可以推到前一个(更少)的位数了啊 显而易见

设推到某处时 i 位数 1 有 j 个 则此时要更新的是 dp[ i ][ j ] (由于本人看见 [j 难受 于是加了空格变成[ j )

我们从上一位数的状态推过来 上一位数的状态则是 dp[ i - 1 ] 那么 j 呢??

按照惯例 肯定是上一位数的所有状态 加上这次更新后的多的状态啦

上一位数的所有状态怎么办呢? 我们把更新后的数分两种情况讨论

第一种 就是继承了之前的所有状态的 但没有新的状态 那怎么得到呢??

前面加 0 !!! 此时存储的状态总数肯定是不变的 (因为数都没变就加了前导 0 ) 但位数变了 就是dp[ i ][ j ] = dp[ i - 1 ][ j ]

第二种 就是这一位数可能为1 此时可能多出好多种情况 但 i 和 j 要和上面的相符

因此 就求上一位的 1 的个数少 1 情况 就是加上这一位的 1 后

1 的总数为 j 的情况 即 dp[ i ][ j ] = dp[ i - 1 ][ j - 1 ]

当然 两者要加起来 就是 dp[ i ][ j ] = dp[ i - 1 ][ j ] + dp[ i - 1 ][ j - 1 ]

这就是递推式啦~~ 然后根据递推式的范围 别忘了预处理哟~

当然本题除了递推式 查询输出也是重点......

首先他位数给出来了呀233 直接暴力加上之前的前缀搜应该也是行的 就从100000000......开始搜 初始化已经得到了dp[ N ][ L ] 然后遇到一个符合的打进去一个 搜到第 i 个退出即可 (仅仅理论 懒死的冰海酱不想打)

但我们考虑比较哲♂学的方法

还是要利用前缀暴力........其实也动了点脑筋的 判断此时第 N 位数是 0 还是 1 这种算法嘛 让吾油来教你好啦——

我们看回递推式 是把当前位数为 0 和 为 1 的 两种情况加起来得到 然后推下去的 然后得到第 i 个数就是答案

那我们能不能以第 i 个数的位置 和他的长度 倒推回去呢 显然可以的 我们知道第 i 个数是第几个 也知道这个位数的第一个数是第几个(就是dp[ i ][ j - 1 ]) 但推到后面可能遇到中间有 00 000 之类的情况 因此 每次更新 如果此时 i ≥ dp[ i ][ j - 1 ] 就说明这个位数是 1 反之是 0 如果是 1 则相应的减去此时的状态数量(dp[ i ][ j - 1 ]) 然后更新( 减 i ) 如果是0就不用减了 直接更新

每次更新记得输出当前位数是 0 还是 1 哦 没必要存数组里了啦~

下放代码(最近比较喜欢缩行2333~)

#include <iostream>
#include <cstdio>
using namespace std;
int dp[1 << 6][1 << 6];
long long n;
int l,k;
int main()
{
	scanf("%d%d%lld",&l,&k,&n);
	for (int a = 0 ; a <= l ; dp[a++][0] = 1);
	for (int a = 0 ; a <= k ; dp[0][a++] = 1);
	for (int a = 1 ; a <= l ; ++ a)
	for (int b = 1 ; b <= l ; ++ b)
	dp[a][b] = dp[a - 1][b] + dp[a - 1][b - 1];
	for (int a = l - 1 ; a >= 0 ; -- a)
	if (dp[a][k] < n) putchar('1'),n -= dp[a][k],--k;
	else putchar('0');putchar('\n');
	return 0;
}

 

3.3.2 Shopping Offers商店购物

题目描述

在商店中,每一种商品都有一个价格(用整数表示)。例如,一朵花的价格是 2 zorkmids (z),而一个花瓶的价格是 5z 。为了吸引更多的顾客,商店举行了促销活动。促销活动把一个或多个商品组合起来降价销售,例如:

  • 三朵花的价格是 5z 而不是 6z,
  • 两个花瓶和一朵花的价格是 10z 而不是 12z。

对于上面的商品信息,购买三朵花和两个花瓶的最少花费是:以优惠价购买两个花瓶和一朵花(10z),以原价购买两朵花(4z)。

编写一个程序,计算顾客购买一定商品的花费,尽量利用优惠使花费最少。尽管有时候添加其他商品可以获得更少的花费,但是你不能这么做。

 

INPUT FORMAT

输入文件包括一些商店提供的优惠信息,接着是购物清单。

 

第一行

优惠商品的种类数(0 <= s <= 99)。
 

第二行..第s+1 行

每一行都用几个整数来表示一种优惠方式。第一个整数 n (1 <= n <= 5),表示这种优惠方式由 n 种商品组成。后面 n 对整数 c 和 k 表示 k (1 <= k <= 5)个编号为 c (1 <= c <= 999)的商品共同构成这种优惠,最后的整数 p 表示这种优惠的优惠价(1 <= p <= 9999)。优惠价总是比原价低。
 

第 s+2 行

这一行有一个整数 b (0 <= b <= 5),表示需要购买 b 种不同的商品。
 

第 s+3 行..第 s+b+2 行

这 b 行中的每一行包括三个整数:c ,k ,和 p 。c 表示唯一的商品编号(1 <= c <= 999),k 表示需要购买的 c 商品的数量(1 <= k <= 5)。p 表示 c 商品的原价(1 <= p <= 999)。最多购买 5*5=25 个商品。

SAMPLE INPUT

2
1 7 3 5
2 7 1 8 2 10
2
7 3 2
8 2 5

 

OUTPUT FORMAT

只有一行,输出一个整数:购买这些物品的最低价格。

SAMPLE OUTPUT

14

 

完全背包=-= 但是好麻烦啊啊啊......

一开始考虑邻接存优惠数组 然后过滤掉不能用的

结果疯狂爆 0 =-= 无奈之下翻标程

没一个是这样的 =-= 突然发现省这么点空间没卵用

然后照搬了下来 =-= 真是羞愧......

这题总共可能买的商品最多五种 好的 那么直接五维数组走起

dp[6][6][6][6][6] 由于下标 0 懒得转换

对于状态转换 5个循环枚举 然后里面套一个可用优惠的循环

现在状态 = min(当前状态 , (现在 - 当前优惠涉及的n种商品 的数量)的状态 + 优惠价)

直接更新 然后有些 0 的......没必要判 直接算进去也是可以的

这题真的是麻烦...... 然后为了压行后的代码更加美观 我又压了一下2333......下放

#include <algorithm>
#include <cstdio>
using namespace std;
int dp[6][6][6][6][6],p[105][1005],num[105],orip[105],a[6],b[6];
struct trades {
	int id,k,r;
} t[6];
int main()
{
	int s,n,c,k;
	scanf("%d",&s);
	for (int i = 1 ; i <= s ; ++ i)
	{
		scanf("%d",&num[i]); for (int j = 1 ; j <= num[i] ; ++ j)
		scanf("%d%d",&c,&k),p[i][c] = k;
		scanf("%d",&orip[i]); // 呐 美观吧 我for都压上去了
	}
	scanf("%d",&n); for (int i = 1 ; i <= n ; ++ i)
	scanf("%d%d%d",&t[i].id,&t[i].k,&t[i].r); //呐 美观吧
	for (a[1] = 0; a[1] <= t[1].k ; ++ a[1])
	for (a[2] = 0; a[2] <= t[2].k ; ++ a[2])
	for (a[3] = 0; a[3] <= t[3].k ; ++ a[3])
	for (a[4] = 0; a[4] <= t[4].k ; ++ a[4])
	for (a[5] = 0; a[5] <= t[5].k ; ++ a[5]) //呐 美观吧
	{
		int tot = 0; //为了防止里面某几行太长 这里加了个定义=-=
		for (int i = 1 ; i <= 5 ; ++ i) tot += a[i] * t[i].r;
		for (int i = 1 ; i <= s ; ++ i)
		{
			if (num[i] > n) continue;
			for (int j = 1 ; j <= n ; ++ j) b[j] = a[j] - p[i][t[j].id];
			if (b[1] < 0 || b[2] < 0 || b[3] < 0 || b[4] < 0 || b[5] < 0) continue;
			tot = min(tot,dp[b[1]][b[2]][b[3]][b[4]][b[5]] + orip[i]);//丑死了这四行=-=
		}
		dp[a[1]][a[2]][a[3]][a[4]][a[5]] = tot;
	}
	printf("%d\n",dp[t[1].k][t[2].k][t[3].k][t[4].k][t[5].k]);
	return 0;
}

 

3.3.4 Home on the Range家的范围

题目描述

农民约翰在一片边长是N (2 <= N <= 250)英里的正方形牧场上放牧他的奶牛。

(因为一些原因,他的奶牛只在正方形的牧场上吃草。)

遗憾的是,他的奶牛已经毁坏一些土地。( 一些1平方英里的正方形)

农民约翰需要统计那些可以放牧奶牛的正方形牧场(至少是2x2的,在这些较大的正方形中没有小于1x1的部分被分割毁坏)。

你的工作要在被供应的数据组里面统计所有不同的正方形放牧区域(>2x2)的个数。

当然,放牧区域可能是重叠。

 

INPUT FORMAT

 

第 1 行:

 

  N,牧区的边长。

 

第 2 到 n+1行:

 

N个没有空格分开的字符。

 

0 表示 "那一个区段被毁坏了";1 表示 " 准备好被吃"。

SAMPLE INPUT

6
101111
001111
111111
001111
101101
111001

 

OUTPUT FORMAT

输出那些存在的正方形的大小和个数,一种一行。

SAMPLE OUTPUT

2 10
3 4
4 1

 

直接搜索爆了 QwQ 还剩一个点是这样的 n = 250 而且全是 1 的特殊情况=-=

于是加了个O2(大罪大孽) 784ms过了=-=

但为了弄透我去搜了百度 然后发现......动规

好吧原本就觉得是动规就是懒得打

先放放暴力搜索的方法 (原创但是原创这种题目的解法并不厉害=-=)

并没有任何实际用处 大忙人们请跳过~~

最主要的还是判断重叠的正方形如何叠加 这里我想了好久=-= 先来看个例子(没加颜色很尴尬)

Tip:原图就是读入哒~

这里为什么要减去呢OwO 因为之前算过的正方形已经包含了

怎么减去呢~~呵 我的o数组用来表示当前点为左上角的正方形的长度的

然后如果当前点更新了 新的o值大于原o值 就加上 怎么加呢

图例(第二个和第三个):o值大了 那大的部分就是没算过的 图中某处由2变成4 那3和4就是新增的

只算3和4 然后推到相邻的 下面和右边的点(原本想直接更新结果发现太麻烦 像上图六个地方重叠)

讲得不详细=-=还是看程序吧 (喂喂你这黑厮..)

#pragma GCC optimize(2)//温馨提示:这句是空的 聪明人都看不见
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = (1 << 8) - 1;
int th[MAX][MAX],ans[MAX],o[MAX][MAX];
inline void r(int &x)//神奇读入
{
	char q = getchar();
	while (q != '0' && q != '1') q = getchar();
	if (q == '0') x = 0; else x = 1;
}
int pd(int x,int y,int len)
{//第一个for:比较新增的y列的每一个数 如果为0(废土地)就退出
	for (int a = x ; a < x + len ; ++ a) if (!th[a][y + len - 1]) return 0;
	for (int a = y ; a < y + len ; ++ a) if (!th[x + len - 1][a]) return 0;
	return 1;//第二个for:比较新增的x列的每一个数 如果为0(废土地)就退出
}//这里th的第[x+len-1][y+len-1]个判断了两次..其实可以把第二个循环条件改成 y+len-1 然而用处不大
void update(int x,int y,int len)
{
	if (o[x][y] >= len) return;//如果原o值大于等于将修改的边长为len的正方形的值 说明之前的计算已经包含了 直接退出
	for (int a = len ; a > o[x][y] ; ++ans[a--]);//当前计算未包含 加上
	o[x][y] = len;//更新o值
	update(x + 1,y,len - 1);//继续推
	update(x,y + 1,len - 1);//继续推
}
void fs(int x,int y)//原本想打dfs觉得不像,bfs也不像,然后打了fs...就是搜索
{
	int a = 0;//初始化当前正方形边长
	while (pd(x,y,++a));//边长每次加1 然后判断...见那个子程序
	if (--a < 2) return;//因为边长增加到判断到废土地的那一行 要-1 然后边长小于2的不计 两句合并
	update(x,y,a);//正方形左上角及边长已确定 开始更新
}
int main()
{
	int n;
	scanf("%d",&n);
	for (int a = 1 ; a <= n ; ++ a)
	for (int b = 1 ; b <= n ; ++ b) r(th[a][b]);//读入点
	for (int a = 1 ; a < n ; ++ a)
	for (int b = 1 ; b < n ; ++ b) fs(a,b);//以每个点为左上角慢慢搜索更新
	for (int a = 2 ; a <= n ; ++ a) if (ans[a]) printf("%d %d\n",a,ans[a]);//输出答案
	return 0;
}

好了好了正经一点 这是dp专题 我们考虑dp (其实我也不信有人会看我那不开O2不特判一千八ms的东西)

然后我发现那...动规居然和我的有意取童♂工之妙

嗯哼 dp[a][b]表示以该处为右下角 得到的最大的正方形

为了防止世界被破坏被重叠的正方形干扰 我们要取以该点往上和往左能延伸的最小边长 取min

然后每个点遍历一遍 把2到该点值都加一遍到ans中 原理和本人的暴力差不多

好啦大概就是这样 下放拍过的代码~

#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = (1 << 8) - 1;
int th[MAX][MAX],dp[MAX][MAX],ans[MAX];
inline void r(int &x)
{
	char q = getchar();
	while (q != '0' && q != '1') q = getchar();
	if (q == '0') x = 0; else x = 1;
}
int main()
{
	int n;
	scanf("%d",&n);
	for (int a = 1 ; a <= n ; ++ a)
	for (int b = 1 ; b <= n ; ++ b) r(th[a][b]);
	for (int a = 1 ; a <= n ; ++ a)
	for (int b = 1 ; b <= n ; ++ b)
	if (th[a][b]) dp[a][b] = min(dp[a - 1][b - 1],min(dp[a - 1][b],dp[a][b - 1])) + 1;
	for (int a = 2 ; a <= n ; ++ a)
	for (int b = 2 ; b <= n ; ++ b)
	for (int p = 2 ; p <= dp[a][b] ; ++ p) ++ans[p];
	for (int a = 2 ; a <= n ; ++ a) if (ans[a]) printf("%d %d\n",a,ans[a]);
	return 0;
}

呐 你看 同样的思路 别人dp的就是快 32ms过了 =-= 而且代码又短又好看

 

3.3.5 A Game游戏

题目描述

有如下一个双人游戏:N(2 <= N <= 100)个正整数的序列放在一个游戏平台上,两人轮流从序列的两端取数,取数后该数字被去掉并累加到本玩家的得分中,当数取尽时,游戏结束。以最终得分多者为胜。

 

编一个执行最优策略的程序,最优策略就是使自己能得到在当前情况下最大的可能的总分的策略。你的程序要始终为第二位玩家执行最优策略。

 

INPUT FORMAT

第一行:正整数N, 表示序列中正整数的个数。
第二行至末尾:用空格分隔的N个正整数(大小为1-200)。

SAMPLE INPUT

6 
4 7 2 9

5 2

 

OUTPUT FORMAT

只有一行,用空格分隔的两个整数: 依次为玩家一和玩家二最终的得分。

SAMPLE OUTPUT

18 11

 

呐 这篇文章超好的 解释也很详细 看懂了就别回来了

 

(刚开始) 哇 游戏啊 NIM 还是 SG 啊 然而我都没学啊......

(心惊胆战地看完题后) 其实我的内心一直毫无波动 =-= 上一行是骗人的 就是个区间dp而已

然后突然想起 某奶牛零食(洛谷的=-=) 去翻了一下 发现我没过 =-= 不行太羞耻了 打完这部分就去做

区间dp 大都是 取小区间内的最优决策 然后推到大区间 就是从 (1,1) (2,2)....到 (1,2) (2,3)...直到(1,n)

小区间里最优决策初始化 就是只有一个单位的地方 如 (1,1) 初始化很好记 大都这样

然后这题和奶牛零食一类的 都是只能取区间两端的点 那很好推嘛 看看奶牛零食

从 (1,1) 推到 (1,2) 就是 (1,1) + v[2] × 2 和 (2,2) + v[1] × 2 两者的最大值

从 (1,2) 推到 (1,3) 就是 (1,2) + v[3] × 3 和 (2,3) + v[1] × 3 两者的最大值

呐 大概就是这样 但这题呢 是两个人共同取的数列啊=-= 他让你维护第二个最优 然而事实上也要维护第一个最优 =-=

初始化就是寻常的在单点的状态上啦 dp[a][a] = v[a] 这个毋容置疑

然后怎么推上去呢 先看看正(cuo)常(wu)思维

当推到2的时候 他只能选一个 然后他又是先手 那就可以选二者之中最大的

第 1 2 个的dp值都是一个点的最大权值 =-=

然后三个选两个 就某两个里面最大的加上个另外一个

四个选三个 里面三个最大的...加上? 不就成了四选三了吗??嗯 是的 因此不能这么推 =-=

那我们考虑先手拿了这个 后手拿了这个......等等 我们为什么要这么推 这不就是模拟了吗

花了我一个下午终于发现了 (呵 笨死了的Frocean 还四维生物呢) ......dp完全是用来存先手的值的 并没确定哪个是先手 只是题目给了 然后我用了这个条件 从而一直纠结 仅此而已 =-=

设 dp[a][b] 为 a 到 b 的区域内 先手能取得的最大的值

怎么推呢 由题意 易得只有两种状态会转移到该状态 分别是 dp[a+1][b] 和 dp[a][b-1] 为了方便 例子就用第一个吧 =-=

呐 先手在 dp[a][b] 中选了第a个 那后手就是这个区间减去第a个 就是 dp[a+1][b] 的区间中选

然后再之前一个状态 dp[a+1][b] 就是先手啦 求出来了 那个状态之前的状态可能是 dp[a+2][b] 或 dp[a+1][b-1]

然后这个又是后手 但在他减 1 的 区间里面 他又是先手 以此类推

因此 dp[a][b]都存的是当前先手取得的值的 所以算到第 dp[1][n] 的时候 存的就是该测试点的先手取得的值

好了最麻烦的概念清楚了 开始推 (例子依然用上面的)

首先我们可以理所当然地得出后手在这区域内的和 sum - dp[a+1][b] 然后加上点权 v[a] 就是第一张情况

然后第二种情况 就是 sum - dp[a][b-1] 然后加上点权 v[b]

sum 一维的点权前缀和 注意 求 a 到 b ( a ≤ b ) 的区域内 是 sum[b] - sum[a - 1] 因为 sum[a] 包含第a个数 不能减去

然后最优肯定是最大值啦 所以推导式就是——

dp[a][b] = max(sum[b] - sum[a] - dp[a + 1][b] + v[a] , sum[b - 1] - sum[a - 1] - dp[a][b - 1] + v[b])

再次提醒 sum要注意 纵使怪处极多 但我真的没错 =-= 下放代码~

#include <algorithm>
#include <cstdio>
using namespace std;
const int MAX = 105;
int dp[MAX][MAX],v[MAX],sum[MAX],n;
int main()
{
	scanf("%d",&n);
	for (int a = 1 ; a <= n ; ++ a) scanf("%d",&v[a]);
	for (int a = 1 ; a <= n ; ++ a) dp[a][a] = v[a];
	for (int a = 1 ; a <= n ; ++ a) sum[a] = sum[a - 1] + v[a];
	for (int len = 1 ; len < n ; ++ len)
	for (int h = 1,t = h + len ; h <= n - len ; ++ h,++ t)
	{
		int p1 = sum[t] - sum[h] - dp[h + 1][t] + v[h];
		int p2 = sum[t - 1] - sum[h - 1] - dp[h][t - 1] + v[t];
		dp[h][t] = max(p1,p2);
	}
	printf("%d %d\n",dp[1][n],sum[n] - dp[1][n]);
	return 0;
}

 

3.4.4 Raucous Rockers“破锣摇滚”乐队

题目描述

你刚刚继承了流行的“破锣摇滚”乐队录制的尚未发表的N(1 <= N <= 20)首歌的版权。你打算从中精选一些歌曲,发行M(1 <= M <= 20)张CD。每一张CD最多可以容纳T(1 <= T <= 20)分钟的音乐,一首歌不能分装在两张CD中。

不巧你是一位古典音乐迷,不懂如何判定这些歌的艺术价值。于是你决定根据以下标准进行选择:

  • 歌曲必须按照创作的时间顺序在CD盘上出现。
  • 选中的歌曲数目尽可能地多。

     

INPUT FORMAT

第一行:三个整数:N, T, M.
第二行:N个整数,分别表示每首歌的长度,按创作时间顺序排列。

SAMPLE INPUT

4 5 2 4 3 4 2

 

OUTPUT FORMAT

一个整数,表示可以装进M张CD盘的乐曲的最大数目。

SAMPLE OUTPUT

3

 

开始直接暴力 =-= 然而听说可以用dp??

细细想想 这些盘可装可不装 然后装盘有容量......不是很像背包吗

于是就是背包了 =-= 冰海酱爽快地确认道

然后读入的歌的长度当做容积 而且没有权重 (或者为 1 ) 这类背包都是要把物品放最外层的

因此 边读入边更新就好

还有 为了防止一次更新加上许多个该种物品 要倒推 这个看推导式自行脑补

dp存歌曲数量哈 因为题目要求输出这个嘛 然后dp[a][b] 表示第 a 个盘 装了 b (请在此处停顿) 分钟 最大的歌曲数

考虑推导方法——

第一种 就是不加 就是不加 =-= dp[a][b] = dp[a][b]

第二种 加 而且没满 那么 dp[a][b] = dp[a][b - len] + 1 然后 len是歌曲长度 加 1 是歌曲数量

第三种 加 而且满了 那么 dp[a][b] = dp[a - 1][t] + 1 然后 t 就是题目输入的 CD容量上限 因为反正你都装不下了 肯定要取上个碟的最大值 那你肯定懒得判断 直接找上界嘛 反正推的时候会更新到 (这就是背包的好处)

最后懒得判断满没满 反正求最大 便三种一起算 同时为了美(suo)观(hang) 第二种和第三种套一个 max 外面第一种再套个 即可

下放dp代码~~ (暴力太丑就丢掉啦)

#include <algorithm>
#include <cstdio>
using namespace std;
int dp[22][22],ans;
int main()
{
	int n,t,m,len;
	scanf("%d%d%d",&n,&t,&m);
	while (n--)
	{
		scanf("%d",&len);
		for (int a = m ; a > 0 ; -- a)
		for (int b = t ; b >= len ; -- b)
		dp[a][b] = max(dp[a][b],max(dp[a - 1][t],dp[a][b - len]) + 1);
	}
	printf("%d\n",dp[m][t]);
	return 0;
}

 

 

 

---------------------------------------第四部分---------------------------------------

4.1.1 Beef McNuggets麦香牛块

题目描述

农夫布朗的奶牛们正在进行斗争,因为它们听说麦当劳正在考虑引进一种新产品:麦香牛块。奶牛们正在想尽一切办法让这种可怕的设想泡汤。奶牛们进行斗争的策略之一是“劣质的包装”。“看,”,奶牛们说,“如果你用只有一次能装3块、6块或10块的三种包装盒装麦香牛块,你就不可能满足想要一次只想买1、2、4、5、7、8、11、14或17块麦香牛块的顾客了。劣质的包装意味着劣质的产品。”

你的任务是帮助这些奶牛。给出包装盒的种类数N(1<=N<=10)和N个代表不同种类包装盒容纳麦香牛块个数的正整数(1<=i<=256),输出顾客不能用上述包装盒(每种盒子数量无限)买到麦香牛块的最大块数。如果在限定范围内所有购买方案都能得到满足,则输出0。

范围限制是所有不超过2,000,000,000的正整数。

 

INPUT FORMAT

第1行:包装盒的种类数N
第2行到N+1行:每个种类包装盒容纳麦香牛块的个数

SAMPLE INPUT

3
3
6
10

 

OUTPUT FORMAT

输出文件只有一行数字:顾客不能用包装盒买到麦香牛块的最大块数或0(如果在限定范围内所有购买方案都能得到满足)。

SAMPLE OUTPUT

17

 

做这题气得我想吃麦香牛块

我一个盒子干嘛要装满啊真的是

好了正题 =-=

首先数论知识 两个数 p 和 q 且 gcd(p,q) = 1 (互质)

那么无法表示为两数的 某个倍数 (可以不等) 的 最大的 数 为 p × q - p - q

虽然我也不太懂 =-= 我只知道范围开到 1 << 18 (就是65536 即256 × 256) 多一点就好

然后如果答案大于 1 << 18 答案换成 0

这是个坑点啊啊啊 有个数据 4 252 250 254 256 答案为 0 如果不判的话 答案根据你的范围随机 (神tm)

好了 这题就是个无尽完全背包 不过dp只用存 能不能到达某个容量 初始化 dp[0] = 1 不用解释了吧 =-=

然后更新 我们用 或 ( | ) 即可 就判断两个里面 有一个 1 就更新 然后 1 遇到 0 就不更新

当然用 max 也可以 if 也可以 喜欢就好 但这样最快~~ 下放代码~~

#include <cstdio>
using namespace std;
const int MAX = 65600;
int dp[MAX + (1 << 4)] = {1},ans;
int main() {
	int n,v;
	scanf("%d",&n);
	for (int a = 1 ; a <= n ; ++ a) {
	scanf("%d",&v);//边读边更新 上一题有 不解释 =-=
		for (int b = v ; b <= MAX ; ++ b)
		dp[b] |= dp[b - v];//或运算
	}
	for (int a = MAX ; a > 0 ; -- a)
		if (!dp[a]) {
		ans = a;
		break;
		}
	if (ans > (1 << 16)) ans = 0;//重要的判断
	printf("%d\n",ans);
	return 0;
}

 

JZOJ1266.【USACO题库】2.3.1 Longest Prefix最长前缀 题目描述在生物学中,一些生物的结构是用包含其要素的大写字母序列来表示的。生物学家对于把长的序列分解成较短的(称之为元素的)序列很感兴趣。如果一个集合 P 中的元素可以通过串联(允许重复;串联,相当于 Pascal 中的 “+” 运算符)组成一个序列 S ,那么我们认为序列 S 可以分解为 P 中的元素。并不是所有的元素都必须出现。举个例子,序列 ABABACABAAB 可以分解为下面集合中的元素: 阅读详情

相关推荐

【USACO题库】2.3.2 Cow Pedigrees奶牛家谱 题解

题目描述 农民约翰准备购买一群新奶牛。 在这个新的奶牛群中, 每一个母亲奶牛都生两小奶牛。这些奶牛间的关系可以用二叉树来表示。这些二叉树总共有N个节点(3 树的高度等于K(1 有多少不同的家谱结构? 如果一个家谱的树结构不同于另一个的, 那么这两个家谱就是不同的。输出可能的家谱树的个数除以9901的余数。 INPUT FORMAT 第1行: 两个空格分开的整数, N和K。 SAMP

Hello,I'm Proking. 2914

算法学习 | 动态规划经典练习题合集

动态规划思想~带权值的最小路径和、背包问题(二)、分割回文串-ii、编辑距离

qq_58284486的博客 3584

USACO 2006 March 题库深入解析与实战

USACO(United States of America Computing Olympiad),美国信息学奥林匹克竞赛,旨在为中学生提供一个展示他们解决计算机科学问题能力的平台。这项竞赛不仅考察参与者的编程技能,还强调算法的理解能力和问题解决的创新思维。奶牛主题问题在USACO竞赛中是一个经典的题型,它通常要求参赛者解决与奶牛相关的各种有趣的问题。这些题目的难度从基础到进阶不等,通常需要参赛者具备一定的逻辑推理能力和编程技能。

weixin_35886636的博客 851

动态规划题目汇总

动态规划题目汇总

雪糕cool的博客 689

USACO信息学竞赛编程题库实战训练

USACO(美国计算机奥林匹克竞赛)是一项面向全球中学生的高水平计算机编程竞赛,其题目以算法设计与实现为核心,涵盖了从基础编程到复杂数据结构与算法的广泛应用。该赛事采用在线评测机制,参赛者需在规定时间内完成若干编程题目,题目难度逐级递增,分为铜组、银组、金组和铂金组四个等级。通过参与USACO训练与比赛,学生不仅能系统提升算法思维与编程能力,还能增强问题建模、时间复杂度分析和代码优化的能力。此外,USACO题库公开、题目质量高,已成为全球信息学竞赛选手的重要训练资源。// 按第二个元素排序});

weixin_35751194的博客 914

USACO编程竞赛题库:数据结构与算法实践

在讨论数组的声明和初始化之前,理解数组是一个能够存储一系列相同类型元素的数据结构是至关重要的。在C++中,数组的声明可以按照以下格式进行:这里type表示数组中元素的数据类型,arrayName是数组的名称,arraySize是数组中元素的数量。数组一旦声明,其大小就是固定的,这意味着我们不能改变数组的总容量。在初始化数组时,我们可以提供初始值,编译器会根据提供的初始值自动确定数组大小:// 声明并初始化一个有5个整数的数组。

weixin_29301059的博客 1003

P3146 [USACO16OPEN]248-动态规划,区间dp

给定一个1*n的地图,在里面玩2048,每次可以合并相邻两个(数值范围1-40),问最大能合出多少。注意合并后的数值并非加倍而是+1,例如2与2合并后的数值为3。 https://www.luogu.org/problemnew/show/P3146 状态:f[i][j]表示i到j能取到的最大值。 当f[i][k]==f[k+1][j]时:f[i][j]=max(f[i][j],f[i]...

LingFengGold 312

动态规划算法经典题目

几道动态规划的经典算法 非常经典 值得分享

【动态规划】【USACO18DEC】Teamwork

题目链接: 【小结】: 这个题,和我想的不太一样,我真的受到昨天的一个题影响了,一直没有想法,后来大家都说是dp,我才反应回来,不难想到就是dp,但是怎么写这个状态方程呢???这就让我很迷糊了。。。做题做得太少了,所以才写不出来。 【题意】: 这个题目挺有意思的,就是农夫John,然后他有n头牛,然后每一头牛都有各自的打包装的本领,然后农夫John分组(最多为k头牛一组)后可以让组内的所有...

Z_sea的博客 652

Linux系统centos7安装网卡驱动

有时候我们装完Centos或Ubuntu系统会发现没有网卡信息,说明网卡驱动不配置,所以我们要重新安装。 首先去服务器主板官网下载网卡驱动,注意32位和64位。 先查看网卡型号 lspci | grep -i ethernet 我的网卡型号是 00:1f.6 Ethernet controller: Intel Corporation Device 0d55。 网上一找,压根没找到这型号的网卡,别说驱动了。怀着试一试的心态,就安装i219-v系的网卡驱动,还别说,成功了。 针对i21...

wshc18的博客 2万+

ESP32-P4实战:LVGL V9.4 PPA加速配置与性能对比

本文详细介绍了如何在ESP32-P4平台上配置LVGL V9.4的PPA硬件加速功能,通过启用PPA加速显著提升图形处理性能并降低CPU占用率。文章包含环境准备、配置步骤、性能对比及常见问题解决方案,帮助开发者高效利用ESP32-P4的PPA加速器优化嵌入式GUI应用。

rice5的博客 1191

普及练习场

普及组选手可冲刺训练,提高组选手亦可在此巩固基础。

p605779的博客 2265

基于stm32f407VGT6控制WS2812的TIM1+PWM+DMA实现方式

项目中使用到了ws2812灯带,作为产品的外观显示灯,经过开发之后,整理一下,仅供大家参考。 WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源。 其外型与一个5050 LED灯珠相同, 每个元件即为一个像素点。 像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路, 还包含有高精度的内部振荡器和可编程定电流控制部分, 有效保证了像素点光的颜色高度一致。数据协议采用单线归零码的通讯方式, 像素点在上电复位以后, DIN端接受从控制器传输过来的数据, 首先送过来的24bit数据被第一个像素

weixin_43608153的博客 9214

ACM大量习题题库

ACM大量习题题库 现在网上有许多题库,大多是可以在线评测,所以叫做Online Judge。除了USACO是为IOI准备外,其余几乎全部是大学的ACM竞赛题库。USACOhttp://ace.delos.com/usacogate美国著名在线题库,专门为信息学竞赛选手准备TJUhttp://acm.tongji.edu.cn/同济大学在线题库,唯一的中文题库,适合NOIP

followingturing 追随图灵的路上... 2848

足球大数据分析入门:从赔率到预测的完整流程(附数据集)

本文提供了一份足球大数据分析的完整实战指南,涵盖从数据采集、清洗、特征工程到模型构建与评估的全流程。文章详细介绍了如何利用公开数据集与API获取数据,并通过逻辑回归、随机森林等模型进行比赛结果预测,最后强调了模型评估与策略回测的重要性,帮助读者构建自己的分析框架。

r7s8t的博客 406

双目相机立体匹配算法概述(含Python代码)

立体匹配任务是计算机视觉中的一个重要问题,旨在从一对具有相同场景的图像中估计每个像素的视差(disparity)。本文对现有的传统算法和深度学习算法进行了综述,以便于各位选择合适的立体匹配算法用于深度估计或三维建模。

m0_59257547的博客 8292

USACO 比赛简介

USA Computing Olympiad比赛简介 usaco是美国中学生的官方竞赛网站。是美国著名在线题库,专门为信息学竞赛选手准备。推荐直接阅读英语原文,既准确可靠又可提高英语水平。做题方式模拟正式比赛,采用标准测评机、文件输入输出、直接提交程序源文件的测评方式。网站的Training题目全面,是学习信息学不可不知的网站,每年NOI,NOIP(或CSP)都会参考上面的题目。每道题附有详细题解,可查看测试数据和运行结果,便于调试、发现错误并改正。采用章节递进的层次结构,由易到难,讲授知识、练习编程结

CWhide的博客 1176

VSCODE 安装LATEX环境,参数配置,常见问题解决

1 准备Texlive安装包 可以从以下网址下载 texlive 的镜像文件: 华为镜像 阿里镜像 官网 网址中大概文件构成 下载完后我们需要的文件构成(我是2020年下载过了,这次就不重新下载了) 将这个镜像文件进行解压 2 安装Texlive 右键install-tl-windows.bat,单击以管理员身份运行,进入安装界面. 点击左下角的“Advanced”进入高级安装来取消你不需要安装的宏包。 点击“Customize”来取消勾选不需要的宏包。 单是把非中英的语言包取消勾选可以省掉 1G

呆呆象呆呆的博客 6万+

【USACO题库】3.3.5 A Game游戏

题目大意: 求在一个序列里(1 样例输入: 6 4 7 2 9 5 2 样例输出: 18 11 解释: 两人依次选的是:2,4,7,5,9,2 第一个人=2+7+9=18 第二个人=4+5+2=11 这道题实质上是一道动态规划类题目,因为两人所选的策略都是最优策略,并不是一个贪心策略。所以,我们可以想到把一个序列来分段,也就是当前两人分别在取的序列的开

Hello,I'm Proking. 1364

40、基于指令的GPU编程:OpenACC与OpenMP的深入解析

本文深入解析了两种基于指令的GPU编程模型——OpenACC和OpenMP。从编程复杂度、数据处理方式和性能表现等多个方面对两者进行了对比分析,并结合实际应用场景提供了选择建议。通过详细的代码示例和性能测试结果,帮助开发者更好地理解和应用这两种编程模型,以充分发挥GPU的并行计算能力。

q5r6s7的博客 134

Raspberry Pi Pico SDK开发-Timer-定时

RP2040 由 Raspberry Pi 设计,具有双核 Arm Cortex-M0+ 处理器和 264KB 内部 RAM,并支持高达 16MB 的片外闪存。 广泛的灵活 I/O 选项包括 I2C、SPI 和独特的可编程 I/O (PIO)。 本实例将演示如何使用Timer的定时功能。

视觉与物联智能 2041

USACO 简介

USACO(美国计算机奥林匹克竞赛)是北美最具影响力的青少年算法竞赛之一,面向中学生,旨在培养计算机编程人才。比赛分为铜级、银级、金级和铂金级四个级别,每年举办多次在线比赛,参赛者需在规定时间内完成编程问题。USACO不仅为大学申请加分,还是美国信息学奥赛选拔的基础,参赛者通过系统学习和大量练习,能显著提升算法思维和解决复杂问题的能力,为未来在计算机科学领域的发展奠定基础。

小宝哥Code的专栏 1044

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

usaco 总结

usaco 总结 Personalized Curriculum for Leo Kan; Last visit: 12 hours ago Congratulations! You have finished all available material. Chapter 1 DONE 2008.03.16 Getting started Chapter 2

eecrazy的专栏 1458

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

1300. 【USACO题库】4.1.4 Cryptcowgraphy解密牛语

这道题目我交了足足 292929 次, AC 率变成 dog 了农民Brown和John的牛们计划协同逃出它们各自的农场。它们设计了一种加密方法用来保护它们的通讯不被他人知道。如果一头牛有信息要加密,比如"International Olympiad in Informatics",它会随机地把C,O,W三个字母插到到信息中(其中C在O前面,O在W前面),然后它把C与O之间的文字和 O与W之间的文字的位置换过来。这里是两个例子: -> -> 为了使解密更复杂,牛们会在一条消息里多次采用这个加密方法(把上次

2401_89382924的博客 365
上一篇: 网络流 最大流 之 最高标号预流推进算法(HLPP) [未完]
下一篇: C++ 杂项 1
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值