20210523普及组训练

18 篇文章 0 订阅
8 篇文章 0 订阅
本文介绍了三道算法题目,分别涉及算式运算、字符串处理和动态规划。第一题通过循环与模拟计算三位数乘两位数的组合数;第二题利用位运算解决字符串操作,形成破碎项链的所有可能;第三题使用动态规划解决零钱兑换问题,计算不同组合方式。文章详细阐述了解题思路和关键代码实现。
摘要由CSDN通过智能技术生成

SSLOJ——5.23套题(From USACO)

1.算式运算

题目描述:

题目描述

输入:

第一行包含整数 N (1≤N≤9)。

第二行包含 N 个整数,表示给定的数字,数据保证这 N 个数字互不相同。

输出:

输出一个整数,表示可以使得算式成立的总填法数量。

样例:

input:
5
2 3 4 6 8
output:
1

思路:

这道题咋一看,嗯?这不是电风扇 (DFS)嘛?别急!再看看:
1.这道题永远是三位数乘两位数
2.计算过程中所有出现的数都由题目所给的N个数组成
3.两个中间值必定是三位数,结果必定是四位数
4.综上所述,这道题难度为循环级别,核心算法是循环与模拟

还不懂?举个栗子:
原谅我的字丑

好了,这道题所有的难点你都知道了,下面看code吧!

code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#define ll long long
#define INF 0x3f3f3f3f

using namespace std;
ll n,ans;
map<ll,bool> ma;
bool check(ll);
bool js(ll,ll);
int main()
{
	//freopen("cal.in","r",stdin);
	//freopen("cal.out","w",stdout);
	ios::sync_with_stdio(false);
	
	cin >> n;
	for(int i = 1,x;i <= n;++i)
	{
		cin >> x;
		ma[x] = true;
	}
	
	
	for(int i = 100;i <= 999;++i)//枚举三位数 
	{
		for(int j = 10;j <= 99;++j)//枚举两位数 
		{
			if(js(i,j)&&check(i)&&check(j)) ++ans; 
		}
	}
	cout << ans << endl;
	return 0;
}

//5
//2 3 4 6 8

bool check(ll x)//判断中间数x是否在序列里面 
{
	while(x)
	{
		if(!ma[x % 10]) return false;
		x /= 10;
	}
	return true;
}


bool js(ll x,ll y)
{
	ll a = x * (y % 10);
	ll b = x * (y / 10);
	ll sum = x * y;
	if(a < 100||a > 999||b < 100||b > 999||sum < 1000||sum > 9999) return false;
	if(!check(a)||!check(b)||!check(sum)) return false;
	return true;
}

2.破碎的项链

题目链接

思路:

难点1.如何破环为链
难点2.如何处理‘w’字符的问题

别急,我们一条条来解决:
1.两倍延长字符串,这样可以在两倍串中枚举出所有的情况
2.可用if判断,也可用位运算,各有各的优缺点:if好想,但写起来较繁琐,且容易漏掉一些情况;位运算很难想,但代码简单,不易出错。
3.这里我用的是位运算,思路如下图所示:

原谅我的字丑

code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#define ll long long
#define INF 0x3f3f3f3f

using namespace std;
ll n,ans;
string s;
ll js(char);
int main()
{
	//freopen("neck.in","r",stdin);
	//freopen("neck.out","w",stdout);
	ios::sync_with_stdio(false);
	
	cin >> n >> s;
	s += s;//两倍延长
	
	for(int i = 0,l,r,tl,tr,sum;i < n;++i)
	{
		l = i,r = i + n - 1;
		tl = 0,tr = 0,sum = 0;
		while(l <= r)
		{
			tl |= js(s[l]);
			if(tl == 3) break;
			++l;
			++sum;
		}
		
		while(l <= r)
		{
			tr |= js(s[r]);
			if(tr == 3) break;
			--r;
			++sum;
		}
		
		ans = max(ans,(ll)sum);
	}
	cout << ans << endl;
	return 0;
}


//w:0,r=1,b=2
//wwwbbrwrbrbrrbrbrwrwwrbwrwrrb

ll js(char c)
{
	if(c == 'r') return 1;
	else if(c == 'w') return 0;
	return 2;
}

3.换零钱(DP)

题目描述:

现要将 N 元钱换为零钱,有多少不同的换法?
币值包括1 2 5分,1 2 5角,1 2 5 10 20 50 100元。
例如:5分钱换为零钱,有以下4种换法:
(1)5个1分;(2)1个2分,3个1分;(3)2个2分,1个1分;(4)1个5分
(由于结果可能会很大,输出Mod 10^9 + 7的结果)

输入:

输入1个数N,N = 100表示1元钱。(1 <= N <= 100000)

输出:

输出 M o d 1 0 9 + 7 Mod 10^9+7 Mod109+7的结果。

思路:

1.确定DP的类型:完全背包求方案数类型。
2.转换到做背包问题的思想,状态怎么定义,怎么转换,阶段是什么,价值、容量是什么?很明显,这道题的阶段是13种币值,价值与容量都是钱数。因此不难定义出状态: f [ i ] 表 示 钱 数 为 i 时 , 方 案 数 m o d 1 0 9 + 7 的 最 大 值 。 f[i]表示钱数为i时,方案数 mod 10 ^ 9+ 7 的最大值。 f[i]imod109+7带回题目,f[n]恰为题目所问。
3.细节:

(1)要将13种币值打个表存下来
(2)一定要初始化,确定DP的边界: f [ 0 ] = 1 f[0] = 1 f[0]=1。即:当钱数为0时,方案数只有一种
(3)不要忘了输出时mod 1e9 + 7

code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define INF 0x3f3f3f3f 
#define mod 1000000007

using namespace std;
ll n,f[100100];
const int b[] = {0,1,2,5,10,20,50,100,200,500,1000,2000,5000,10000};//13
int main()
{
	//freopen("money.in","r",stdin);
	//freopen("money.out","w",stdout);
	ios::sync_with_stdio(false);
	
	cin >> n;
	f[0] = 1;
	for(int i = 1;i <= 13;++i)
	{
		for(int j = b[i];j <= n;++j)
		{
			f[j] += (f[j - b[i]] % mod);//加法原理,总数为转移之前加上转移之后
		}
	}
	cout << f[n] % mod << endl;
	return 0; 
}

//单位:分!(=1/100元) 

4.战场侦察(还没写完)

-------------------------The end---------------------------------

thanks for your watching!
hope this blog can help you!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值