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]表示钱数为i时,方案数mod109+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!