题目跳转:
题目描述
小明的班上共有 n 元班费,同学们准备使用班费集体购买 3 种物品:
- 圆规,每个 7 元。
- 笔,每支 4 元。
- 笔记本,每本 3 元。
小明负责订购文具,设圆规,笔,笔记本的订购数量分别为 a,b,c,他订购的原则依次如下:
- n 元钱必须正好用光,即 7a+4b+3c=n。
- 在满足以上条件情况下,成套的数量尽可能大,即 a,b,c中的最小值尽可能大。
- 在满足以上条件情况下,物品的总数尽可能大,即 a+b+c 尽可能大。
请你帮助小明求出满足条件的最优方案。可以证明若存在方案,则最优方案唯一。
输入格式
输入仅一行一个整数,代表班费数量 n。
输出格式
如果问题无解,请输出 -1。
否则输出一行三个用空格隔开的整数 a, b, c,分别代表圆规、笔、笔记本的个数。
输入输出样例
输入 #1
1
输出 #1
-1
输入 #2
14
输出 #2
1 1 1
输入 #3
33
输出 #3
1 2 6
说明/提示
样例输入输出 3 解释
a=2,b=4,c=1 也是满足条件 1,2 的方案,但对于条件 3,该方案只买了 7 个物品,不如 a=1,b=2,c=6 的方案。
数据规模与约定
-
对于测试点 1 ~ 6,保证n≤14。
-
对于测试点 7 ~ 12,保证 n是 14 的倍数。
-
对于测试点 13 ~18,保证 n≤100。
-
对于 100% 的数据,保证 0≤n≤\(10^5\)。
题解部分
【题目大意】给定三件物品a,b,c的价格以及总金额n,求问订购原则的情况下,最优的购买方案是什么?
【思路分析】如果在分析题目样例的时候,能够人工模拟拼凑出它的购买方案,那不难想到使用枚举算法解决。但是你全部枚举a,b,c的话肯定会超时的,所以我们还需要对枚举算法进行一定的优化。那么怎么来优化呢?
从题目的三个条件来入手:
- n 元钱必须正好用光,即 7a+4b+3c=n。
- 在满足以上条件情况下,成套的数量尽可能大,即 a,b,c中的最小值尽可能大。
- 在满足以上条件情况下,物品的总数尽可能大,即 a+b+c 尽可能大。
首先,第一个条件,n元钱必须用完,那就要我们保证不能剩下钱,这里没什么好优化的。
再来看第二个条件,成套的数量尽可能大,那就是说明尽可能地整体买入这三样东西,他们的总额是14元。那么我们可以先处理一个maxt,maxt=n/14,代表n元钱最多能卖maxt套。接下来就可以从maxt枚举到0,看有没有合法方案。
最后,第三个条件,物品总数尽可能大,总数尽可能多,那么我就要尽可能买价格便宜的,那才能尽可能多。所以买完一整套后,剩余的money,money=n-(maxt*14),就先全部买入c,套数为maxc=money/3。接着再循环maxc~0,其中b的套数就可以通过计算得到,就不要枚举了,其实这道题有点像百钱买百鸡的变形。
通过以上几个条件的约束,枚举范围就变得很小了,就可以通过此题,并且注意枚举都是从大到小倒着枚举的
Talk is cheap,上代码
【参考代码】
#include <bits/stdc++.h>
using namespace std;
int n,ta,tb,tc; //n为总的金钱,ta,tb,tc存买的套数
int money,moneyb,maxc; //money是整套买了之后的剩下的钱,c是枚举的笔和笔记本套数,moneyb是买了a、c之后剩下的钱
int main()
{
int flag=0; //0代表当前没有方案
cin>>n;
int maxt=n/14; //为了满足第二个条件,先3件物品整套买
for(int i=maxt;i>=0;i--){ //从最大套数枚举到最小
ta = tb = tc = i ; //套数初始为i
money=n-(i*14); //算出剩下的钱
maxc=money/3; //剩下的钱能买入的最多c的套数
for(int c=maxc;c>=0;c--){ //为了满足第三个条件,那么就要让3块钱的物品尽可能的多
moneyb=(money-3*c); //算出能够买下物品b剩余的钱
if(moneyb%4==0){ //如果剩下的钱能买完物品b
flag=1; //那么就能产生方案
if(3*i+moneyb/4+c>ta+tb+tc){ //更新ta,tb,tc的值
ta=i;
tb=i+moneyb/4;
tc=i+c;
}
}
if(flag){ //输出方案
printf("%d %d %d\n",ta,tb,tc);
return 0;
}
}
}
printf("-1"); //否则没有方案
return 0;
}
题目描述
小 H 是一个热爱运动的孩子,某天他想给自己制定一个跑步计划。小 H 计划跑 \(n\)米,其中第$ i(i \geq 1)$ 分钟要跑 \(x_i\)米($x_i $是正整数),但没有确定好总时长。
由于随着跑步时间增加,小 H 会越来越累,所以小 H 的计划必须满足对于任意 \(i(i >1)\) 都满足$ x_i \leq x_{i-1}$。
现在小 H 想知道一共有多少个不同的满足条件的计划,请你帮助他。两个计划不同当且仅当跑步的总时长不同,或者存在一个 \(i\),使得两个计划中 \(x_i\) 不相同。
由于最后的答案可能很大,你只需要求出答案对 \(p\) 取模的结果。
输入格式
输入只有一行两个整数,代表总米数 \(n\) 和模数 \(p\)。
输出格式
输出一行一个整数,代表答案对 \(p\) 取模的结果。
输入输出样例
输入 #1
4 44
输出 #1复制
5
输入 #2
66 666666
输出 #2
323522
输入 #3
66666 66666666
输出 #3
45183149
说明/提示
样例输入输出 1 解释
五个不同的计划分别是:{1,1,1,1},{2,1,1},{3,1},{2,2},{4}。
数据规模与约定
本题共 10 个测试点,各测试点信息如下表。
测试点编号 | \(n \leq\) | 测试点编号 | \(n \leq\) |
---|---|---|---|
1 | 5 | 6 | \(2\times 10^3\) |
2 | 10 | 7 | \(5\times 10^3\) |
3 | 50 | 8 | \(2\times 10^4\) |
4 | 100 | 9 | \(5\times 10^4\) |
5 | 500 | 10 | \(10^5\) |
对于全部的测试点,保证$ 1 \leq n \leq 10^5$,\(1 \leq p < 2^{30}\)。
题解部分
【题目大意】给出一个数n,求多少种拆分方案?
【思路分析】裸的拆分数问题,这道题的满分解法超出了大家的知识范围,建议大家可以去做简单版【FZOJ 1297 数的划分】一题。
满分解法要求时间复杂度控制在\(O(n\sqrt(n))\)
满分解法一:完全背包DP+分块
满分解法二:数学知识——五边形数定理
【参考代码】
下面代码借鉴自洛谷
解法一:
//完全背包+分块
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int f[N];
int g[400][N];
int main() {
int n, p;
cin >> n >> p;
int m = sqrt(n) + 1;
f[0] = 1;
for (int i = 1; i < m; i++) {
for (int j = i; j <= n; j++) {
f[j] += f[j - i];
f[j] %= p;
}
}
g[0][0] = 1;
for (int i = 1; i < m; i++) {
for (int j = i; j <= n; j++) {
g[i][j] = g[i][j - i];
if (j >= m) g[i][j] += g[i - 1][j - m];
g[i][j] %= p;
}
}
int ans = 0;
for (int i = 0; i <= n; i++) {
LL sum = 0;
for (int j = 0; j < m; j++) sum += g[j][n - i];
sum %= p;
ans = (ans + f[i] * sum) % p;
}
cout << ans << endl;
return 0;
}
解法二:
//五边形数定理
#include<bits/stdc++.h>
int T,n,m,mod,f[100010],g[100010];
int main(){
f[0]=1,scanf("%d%d",&n,&mod);
for(int i=1;i*(3*i-1)/2<=n;i++)g[m++]=i*(3*i-1)/2,g[m++]=i*(3*i+1)/2;
for(int i=1;i<=n;i++)for(int j=0;j<m&&g[j]<=i;j++)f[i]=(f[i]+(((j>>1)&1)?-1ll:1ll)*f[i-g[j]])%mod;
printf("%d\n",(f[n]+mod)%mod);
}
题目描述
C 国由 \(n\) 座城市与 \(m\) 条有向道路组成,城市与道路都从 1 开始编号,经过 \(i\) 号道路需要 \(t_i\) 的费用。
现在你要从 1 号城市出发去 \(n\) 号城市,你可以施展最多 \(K\) 次魔法,使得通过下一条道路时,需要的费用变为原来的相反数,即费用从 \(t_i\) 变为 -\(t_i\)。请你算一算,你至少要花费多少费用才能完成这次旅程。注意:使用魔法只是改变一次的花费,而不改变一条道路自身的$ t_i$;最终的费用可以为负,并且一个城市可以经过多次(包括 \(n\) 号城市)。
输入格式
从文件 magic.in 中读入数据。
第一行三个整数$ n, m, K$,表示城市数、道路数、魔法次数限制。
接下来 m 行每行三个整数 \(u_i,v_i,t_i\),第 \(i\) 行描述 \(i\) 号道路,表示一条从 \(u_i\) 到 \(v_i\) 的有向道路,经过它需要花费 \(t_i\)。
输出格式
输出到文件 magic.out 中。
仅一行一个整数表示答案。
样例1输入
4 3 2
1 2 5
2 3 4
3 4 1
样例1输出
-8
样例1解释
依次经过 1 号道路、2 号道路、3 号道路,并在经过 1、2 号道路前使用魔法。
样例2输入
2 2 2
1 2 10
2 1 1
样例2输出
-19
样例2解释
依次经过 1 号道路、2 号道路、1 号道路,并在两次经过 1 号道路前都使用魔法。
数据范围与提示
对于所有测试点和样例满足:
1 ≤ n ≤ 100,1 ≤ m ≤ 2500,0 ≤ K ≤ 106,1 ≤ ui,vi ≤ n,1 ≤ ti ≤ 109。
数据保证图中无自环,无重边,至少存在一条从 1 号城市到达 n 号城市的路径。
每个测试点的具体限制见下表:
题解部分
【前置知识】快速幂、矩阵乘法、最短路floyed
【题目大意】n个城市,m条路,构成一张图。但是在这张图上,边权允许有k次改变,问最少的花费(最短路)。
【思路分析】一眼看完题,心中大呼:这不就是分层图裸题吗?
后来稍微分析了一下时间复杂度和数据规模,特别是看了k的最大值\(10^6\),显然分层图不可行,时间和空间都会爆炸。
这道题目的时间复杂度肯定不能与\(k\)相关,或者尽可能把\(k\)
变成\(\sqrt k\)或者\(\log k\),这样才能保证通过。
所以就有了Floyed求最短路+矩阵优化快速幂的做法
【参考代码】
下面代码借鉴自洛谷
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
struct edge
{
int u,v,w;
}e[2505];
int n,m,k;
struct mat
{
long long a[105][105];
mat(int x=63)
{
memset(a,x,sizeof(a));
}
mat operator*(const mat&b)const
{
mat ans;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans.a[i][j]=min(ans.a[i][j],a[i][k]+b.a[k][j]);
return ans;
}
}a;
long long f[105][105];
mat fpow(mat x,int y)
{
mat ans;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans.a[i][j]=f[i][j];
while(y)
{
if(y&1)ans=ans*x;
x=x*x;
y>>=1;
}
return ans;
}
int main()
{
memset(f,63,sizeof(f));
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
f[i][i]=0;
for(int i=1;i<=m;i++)
{
cin>>e[i].u>>e[i].v>>e[i].w;
f[e[i].u][e[i].v]=e[i].w;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
for(int k=1;k<=m;k++)
{
int u=e[k].u,v=e[k].v,w=e[k].w;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a.a[i][j]=min(a.a[i][j],min(f[i][j],f[i][u]+f[v][j]-w));
}
if(k==0)cout<<f[1][n]<<endl;
else cout<<fpow(a,k).a[1][n]<<endl;
return 0;
}