闲话:
1、就难度而言,本次比较简单,应该可以轻松做完4到5题。考了贪心、模拟、矩阵快速幂、二进制的相关知识。
2、就体验而言,题面不好看,解释的较繁琐,差评。
3、本次做题十分快乐。
题目
1、试题 算法提高 交换Easy
解析:在读入数据后,可以通过swap(l,r)直接交换位置的值;当然也可以手写交换。
#include
using namespace std;
int n,m;
int a[1005];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
while(m--){
int l,r; cin>>l>>r;
swap(a[l],a[r]);
}
for(int i=1;i<=n;i++) cout<<a[i]<<"\n";
}
----------------------------------------------------------------------------------------------------------------------------
2、试题 算法提高 矩阵乘方
解析:这是一道矩阵快速幂裸题,当然也可以模拟一路乘下去,应该也是可以的。直接上模板就可以了。
#include
using namespace std;
typedef long long ll;
ll n,k,mod;
const ll N=105;
struct mat{
ll a[N][N];
};
mat multi(mat a,mat b,ll n){
mat c;
memset(c.a,0,sizeof(c.a));
for(ll i=0;i<n;i++){
for(ll j=0;j<n;j++){
for(ll k=0;k<n;k++){
c.a[i][j]+=a.a[i][k]*b.a[k][j];
c.a[i][j]%=mod;
}
}
}
return c;
}
mat pow(mat c,ll n,ll k){
mat res;
for(int i=0;i<n;i++) res.a[i][i]=1;
while(k){
if(k&1) res=multi(res,c,n);
c=multi(c,c,n);
k>>=1;
}
return res;
}
int main(){
scanf("%lld%lld",&n,&mod);
mat c;
for(ll i=0;i<2;i++){
for(ll j=0;j<2;j++){
scanf("%lld",&c.a[i][j]);
}
}
mat ans=pow(c,2,n);
for(ll i=0;i<2;i++){
for(ll j=0;j<2;j++){
printf("%lld ",ans.a[i][j]%mod);
}
printf("\n");
}
}
----------------------------------------------------------------------------------------------------------------------------
3、试题 算法提高 打水问题
解析:很明显这是一道贪心题,为了使所有人的总等待时间最少。只需要我们在每次有空的龙头的时候,在当前还没有去打水的集合中,让打水需要时间最少的人去打水。这样总等待时间将最少。
----------------------------------------------------------------------------------------------------------------------------
写法一:
#include
using namespace std;
int n,m;
int ti[1005];
int a[1005];
int main(){
cin>>n>>m;
for(int i=0;i<n;i++) cin>>ti[i];
sort(ti,ti+n); //打水顺序按大水需要时间从小到大排序
for(int i=0;i<m;i++) a[i]=ti[i]; //最开始m个龙头都没有人用,所以最开始的m个人没有等待时间
int sum=0;
for(int i=m;i<n;i++){
sum+=a[i%m]; //此时每个人都将分配到其对应的第i%m个龙头,“类似于将人按顺序使用龙头(可以自己理解一下)”
a[i%m]+=ti[i]; //类似前缀和,即当下一次需要使用第i%m个龙头时,此时已经等待的时间
}
cout<<sum<<"\n";
}
----------------------------------------------------------------------------------------------------------------------------
写法二:
#include
using namespace std;
typedef long long ll;
const int maxn = 1010;
int n, m;
int t[maxn];
int sum[maxn];
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> t[i];
sort(t, t + n);
for (int i = 0; i < n; i++) {
sum[i % m] += t[i] * ((n - 1 - i) / m);
}
int ans = 0;
for (int i = 0; i < m; i++) {
ans += sum[i];
}
cout << ans << endl;
}
----------------------------------------------------------------------------------------------------------------------------
4、试题 算法训练 薪水计算
解析:无。直接根据题意分情况讨论就行了
#include
using namespace std;
int main()
{
double t,m,sum;
cin>>t>>m;
if(t<=40) sum=t*m;
else if(t>40&&t<=50) sum=40*m+(t-40)*m*1.5;
else sum=40*m+10*m*1.5+(t-50)*m*2;
cout<<setiosflags(ios::fixed)<<setprecision(2)<<sum;
}
----------------------------------------------------------------------------------------------------------------------------
5、试题 算法训练 黑色星期五
解析:
1、写过类似日期的题目聚聚应该知道有一个根据年月日求星期几的公式----基姆拉尔森计算公式。知道这个公式这题就比较容易解决了。(这里不细说)
学习链接:C语言根据日期判断星期几(使用基姆拉尔森计算公式)
2、模拟,肯定是模拟,虽然这题我没用模拟,我直接上的公式 ,但模拟肯定是可以过的。模拟先从1998年1月1日开始,每次加上当前年份的天数到目标年份的前一年,然后将总天数%7得到某个值x,然后目标年份1月1日是星期几也就是星期四的后x天是星期几。
----------------------------------------------------------------------------------------------------------------------------
写法一:公式
#include
using namespace std;
int year,month,day,sum=0,w;
int jisuan(int year,int month,int day){
if(month==1||month==2){
month+=12;
year--;
}
return (day+2*month+3*(month+1)/5+year+year/4-year/100+year/400)%7+1;
}
int main(){
cin>>year;
day=13;
for(int i=1;i<=12;i++){
month=i;
w=jisuan(year,month,day);
if(w==5) sum++;
}
cout<<sum<<"\n";
}
----------------------------------------------------------------------------------------------------------------------------
写法二:模拟年份
#include
using namespace std;
int r[] = {
0,31,28,31,30,31,30,31,31,30,31,30,31 };
int x = 3;
int sum = 0;
bool check(int x)
{
if (x % 4 == 0 && x % 100 != 0 || x % 400 == 0)return true;
else return false;
}
int main()
{
int year;
scanf("%d", &year);
int f = 0;
for (int i = 1998; i < year; i++)
{
f += 365;
if (check(i))f++;
}
int h = f % 7;
x += h;
if (x % 7 == 0)x = 7;
if (check(year))r[2] = 29;
for (int j = 1; j <= 12; j++)
{
for (int k = 1; k <= r[j]; k++)
{
x++;
int h = x % 7;
if (h == 0)x = 7;
else x = h;
if (k == 13 && x == 5)
sum++;
}
}
printf("%d\n", sum);
}
----------------------------------------------------------------------------------------------------------------------------
写法三:模拟每月
#include
using namespace std;
int r[] = {
0,31,28,31,30,31,30,31,31,30,31,30,31 };
int x = 3;
int sum = 0;
bool check(int x)
{
if (x % 4 == 0 && x % 100 != 0 || x % 400 == 0)return true;
else return false;
}
int main()
{
int year;
scanf("%d", &year);
for (int i = 1998; i <= year; i++)
{
if (check(i))r[2] = 29;
else r[2] = 28;
for (int j = 1; j <= 12; j++)
{
for (int k = 1; k <= r[j]; k++)
{
x++;
int h = x % 7;
if (h == 0)x = 7;
else x = h;
if (i == year && k == 13 && x == 5)
sum++;
}
}
}
printf("%d\n", sum);
}
----------------------------------------------------------------------------------------------------------------------------
6、试题 算法训练 和为T
解析:
二进制选取情况,第i位为1就选a[i],为0就不选a[i]。
由于每种选取情况必须选一个,即不能出现都没有选取的情况(二进制:0)。
总选取情况从1到(1<所以时间是够用的,不必担心。
#include
using namespace std;
int n,m,num;
int a[25];
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
cin>>m;
for(int i=1;i<(1<<n);i++){
int x=i;
int sum=0;
for(int j=0;j<n;j++){
if(x&(1<<j)) sum+=a[j];
}
if(sum==m){
num++;
for(int j=0;j<n;j++){
if(x&(1<<j)) cout<<a[j]<<" ";
}
puts("");
}
}
cout<<num<<"\n";
}
----------------------------------------------------------------------------------------------------------------------------
总结
1、本场题目较水,平时多刷题的同学应该深有体会。
2、某些特定类型的题目都要一些便捷的解法,平时可以多进行专题训练。
3、学习虽然重要,但还是得加强身体锻炼。比赛长达4小时,有的长达5小时,这还真不是一件容易的事。加强锻炼,在比赛等其他方面才更有优势。(抄上篇的,编不出了)