第十关——CCF线上赛 普及组
14:30:35 背影是真的,人是假的,没什么执着。——王菲《百年孤寂》
第一题 文具订购
题目描述
小明的班上共有 n 元班费,同学们准备使用班费集体购买 3 种物品:
-
圆规,每个 77 元。
-
笔,每支 44 元。
-
笔记本,每本 33 元。
小明负责订购文具,设圆规,笔,笔记本的订购数量分别为 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 \leq 14n≤14。
-
对于测试点 7∼12,保证 n 是 14的倍数。
-
对于测试点 13∼18,保证 n≤100。
-
对于全部的测试点,保证 0≤n≤100000。
这道题可以用暴力枚举, 由7a+4b+3c=n可知,a是abc中最小的一个数,一定小于n/14,于是这就是第一个循环,第二个循环与第三个循环的条件上限我是选的最小的一个写,然后就是一个简单的if语句输出后就直接return,防止有两组情况,因为 a,b,c 中的最小值尽可能大,所以第一个循环是从大到小--。
#include<bits/stdc++.h> using namespace std; int main() { int n; scanf("%d",&n); if(n==0) { printf("0 0 0"); return 0; } for(int i=n/14;i>=0;i--) for(int j=i;j<=n/3;j++) for(int k=i;k<=n/3;k++) { if(7*i+4*j+3*k==n) { printf("%d %d %d",i,j,k); return 0; } } printf("-1"); return 0; }
第二题 跑步
题目描述
小 H 是一个热爱运动的孩子,某天他想给自己制定一个跑步计划。小 H 计划跑 nn 米,其中第 i(i \geq 1)i(i≥1)分钟要跑 x_ixi 米(x_ixi 是正整数),但没有确定好总时长。
由于随着跑步时间增加,小 H 会越来越累,所以小 H 的计划必须满足对于任意 i(i >1)i(i>1) 都满足 x_i \leq x_{i-1}xi≤xi−1。
现在小 H 想知道一共有多少个不同的满足条件的计划,请你帮助他。两个计划不同当且仅当跑步的总时长不同,或者存在一个 ii,使得两个计划中 x_ixi 不相同。
由于最后的答案可能很大,你只需要求出答案对 pp 取模的结果。
输入格式
输入只有一行两个整数,代表总米数 nn 和模数 pp。
输出格式
输出一行一个整数,代表答案对 pp 取模的结果。
输入输出样例
输入 #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}。
数据规模与约定
本题共 1010 个测试点,各测试点信息如下表。
测试点编号 |
n ≤ |
测试点编号 |
n ≤ |
---|---|---|---|
11 |
55 |
66 |
2\times 10^32×103 |
22 |
1010 |
77 |
5\times 10^35×103 |
33 |
5050 |
88 |
2\times 10^42×104 |
44 |
100100 |
99 |
5\times 10^45×104 |
55 |
500500 |
1010 |
10^5105 |
对于全部的测试点,保证1≤n≤10^5,1≤p<2^30。
70分代码就是找规律然后递推???但是我听他们说这叫DP???,不管了,放代码
#include<bits/stdc++.h> #define ll long long using namespace std; ll f[100001]; int n,p; int main() { scanf("%d%d",&n,&p); f[0]=1; for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) f[j]=f[j]%p+f[j-i]%p; printf("%d",f[n]); return 0; }
真的是短到我无话可说!
100分代码是标准dp,是一个二维dp,思路差不多吧,注意最后要清空o的值。(经常忘然后就会检查好久好久!!!)
#include<bits/stdc++.h> #define ll long long using namespace std; ll d[100001],f[1001][100001]; int main() { int n,p; scanf("%d%d",&n,&p); int m=sqrt(n)+1; d[0]=f[0][0]=1; for(int i=1;i<m;i++) { for(int j=i;j<=n;j++) { d[j]=(d[j]+d[j-i])%p; } } for(int i=1;i<m;i++) { for(int j=i;j<=n;j++) { f[i][j]=f[i][j-i]; if(j>=m) f[i][j]=(f[i][j]+f[i-1][j-m])%p; } } ll ans=0,o=0; for(int i=0;i<=n;i++) { for(int j=0;j<m;j++) { o=(o+f[j][n-i])%p; } ans+=o*d[i]; ans%=p; o=0; } printf("%d",ans); return 0; }
第三题 魔法
题目描述
C 国由 n 座城市与 m 条有向道路组成,城市与道路都从 1 开始编号,经过 i 号道路需要 ti 的费用。
现在你要从 1 号城市出发去 n 号城市,你可以施展最多 k 次魔法,使得通过下一条道路时,需要的费用变为原来的相反数,即费用从 ti 变为−ti。请你算一算,你至少要花费多少费用才能完成这次旅程。
注意:使用魔法只是改变一次的花费,而不改变一条道路自身的 ti;最终的费用可以为负,并且一个城市可以经过多次(包括 n 号城市)。
输入格式
输入的第一行有三个整数,分别代表城市数 n,道路数 m 和魔法次数限制 k。
第 2 到第(m+1) 行,每行三个整数。第 (i+1) 行的整数 ui,vi,ti 表示存在一条从 ui 到 vi 的单向道路,经过它需要花费ti。
输出格式
输出一行一个整数表示最小的花费。
输入输出样例
输入 #1
4 3 2
1 2 5
2 3 4
3 4 1
输出 #1
-8
输入 #2
2 2 2
1 2 10
2 1 1
输出 #2
-19
说明/提示
输入输出样例 1 解释
依次经过 1 号道路、2 号道路、3 号道路,并在经过 1,2 号道路前使用魔法。
输入输出样例 2 解释
依次经过 1 号道路、2号道路、1 号道路,并在两次经过 1号道路前都使用魔法。
SPFA80分代码,改了很久,思路不是特别清楚,但是还是超时(注意变量赋值一定要大!!!很大很大的那种)
#include<bits/stdc++.h> #define ll long long using namespace std; typedef pair <int,int> P; int n,m,k,h[102],vis[102][200004]; ll d[102][200004]; queue <P> q; struct sby{ int to,nt,len; } e[1000003]; void spfa() { for(int i=1; i<=n; i++) for(int j=0; j<=k; j++) d[i][j]=999999999; d[1][0]=0; q.push(P(1,0)); vis[1][0]=1; while(!q.empty()) { int a=q.front().first; int x=q.front().second; q.pop(); vis[a][x]=0; for(int i=h[a];i;i=e[i].nt) { int y=e[i].to,z=e[i].len; if(d[y][x]>d[a][x]+z) { d[y][x]=d[a][x]+z; if(!vis[y][x]) { q.push(P(y,x)); vis[y][x]=1; } } if(x<k && d[y][x+1]>d[a][x]-z) { d[y][x+1]=d[a][x]-z; if(!vis[y][x+1]) { q.push(P(y,x+1)); vis[y][x+1]=1; } } } } } int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1; i<=m; i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); e[i].to=y; e[i].nt=h[x]; e[i].len=z; h[x]=i; } spfa(); ll ans=999999999; for(int i=0; i<=k; i++) ans=min(ans,d[n][i]); printf("%lld\n",ans); return 0; }
正在想办法可不可以利用一些手段省一些时间复杂度,不用把整篇代码都改了