问题 A: 圆:
这是一个数学题,画图可得,4 个圆时,分割成 14 个区域,可以推导出结论:当圆为0个时,区域数为1个,当圆有x个的时候,区域数有x*x-x+2;
#include<bits/stdc++.h> using namespace std; #define int long long int n; signed main(){ int a,b;//a为圆的个数,b为区域数 cin>>n; while(n--) { cin>>a; if(a==0) b=1; else b=a*a-a+2; cout<<b<<" "; } return 0; }
问题 B: 旅人分宝:
这是一个经典的海盗分金问题,每个旅人只会选择一个对自己最有利的情况,并且要求50%以上的同意,所以当老大给一半的人每一个人一个金币,他们就会同意了。
#include<bits/stdc++.h> using namespace std; int t; int n,x; int main() { cin>>t; while(t--) { cin>>n>>x; if(x%2==0) { cout<<n-x/2+1<<endl; } else { cout<<n-x/2<<endl; } } return 0; }
问题 C: A*B:
经典的高精度乘法:
#include <stdio.h> #include <string.h> //翻转函数 void reverse (int d[510], int len) { int temp, mid = len/2; for (int i = 0, j = len - 1; i < mid, j >= mid; i++, j--) { temp = d[i]; d[i] = d[j]; d[j] = temp; } } int main () { char s1[2005], s2[2005]; int a[2005], b[2005], c[4010] = {0}; scanf("%s", s1); scanf("%s", s2); int lena = strlen(s1); int lenb = strlen(s2); int lenc = lena + lenb; //将char的1转为int的1 for (int i = 0; i < lena; i++) { a[i] = s1[i] - '0'; } for (int i = 0; i < lenb; i++) { b[i] = s2[i] - '0'; } reverse(a, lena); reverse(b, lenb); //核心代码 for (int i = 0; i < lenb; i++) { for (int j = 0; j < lena; j++) { c[i+j] += b[i] * a[j]; c[i+j+1] += c[i+j] / 10; c[i+j] = c[i+j] % 10; } } //处理前导0 lenc = lenc - 1; while (c[lenc] == 0 && lenc >= 1) { lenc--; } //输出 for (int i = lenc; i >= 0; i--) { printf("%d", c[i]); } return 0; }
问题 D: 时间加速器:
由于数据比较大,所以可以用二分,用二分查找,可以定位到确定的时间,大大节省了时间。
#include<iostream> #define int long long using namespace std; int n,a,b,x[600006]; bool check(int mid)//判断函数 { int tot=0; for(int i=1;i<=n;i++) { if(x[i]<=mid*a)continue; else { if((x[i]-mid*a)%b==0)tot+=(x[i]-mid*a)/b; else tot+=((x[i]-mid*a)/b+1); } } return tot<=mid; } signed main() { cin>>n>>a>>b; for(int i=1;i<=n;i++)cin>>x[i]; int l=0,r=1e9,mid; while(l<=r)//二分搜索 { mid=(l+r)/2; if(check(mid))r=mid-1; else l=mid+1; } cout<<l<<endl; return 0; }
问题 E: 切巧克力:
输入
4 2
4
5 6 1
输出
19
如果先切横的,结果就是1+2∗4+2∗5+2∗6=311+2∗4+2∗5+2∗6=31。
如果先切竖的,结果就是4+5+6+1∗4=194+5+6+1∗4=19。
显然先切竖的更优。
由此,可以得出一个结论:先切代价大的。
于是,思路就出来了。先把两个数组从大到小排序,然后两个数组挨个比较,较大的就在答案上加上这个代价(要乘上切了多少刀),再把指针往后挪一位,直到全部算完即可。
#include<iostream> #include<cmath> #include<algorithm> using namespace std; int n, m; int a[10005], b[10005];//横线和竖线的代价 int main() { cin >> n >> m; int s1 = 1, s2 = 1;//s1和s2可以是a数组和b数组的指针,也可以是横纵分成的块数 for (int i = 1; i <= n-1; i++) { cin >> a[i]; } for (int i = 1; i <= m - 1; i++) { cin >> b[i]; } for(int i=1;i<=n-1;i++) { for(int j=1;j<=n-1-i;j++) { if(a[j]<a[j+1]) { int t; t=a[j]; a[j]=a[j+1]; a[j+1]=t; } } } for(int i=1;i<=m-1;i++) { for(int j=1;j<=m-1-i;j++) { if(b[j]<b[j+1]) { int t; t=b[j]; b[j]=b[j+1]; b[j+1]=t; } } } long long ans = 0; for (int i = 2; i < n + m; i++) { if (a[s1] > b[s2]) ans += s2 * a[s1++];//选择大的,这里s2表示块数,s1指针后移一位 else ans += s1 * b[s2++];//同理,和上面相反 } cout << ans << endl; return 0; }
问题 F: 建房子:
构建一个01背包。
思路是将体力看做背包 将石子看做物品 然后就很自然地得出了本题的状态转移方程:
f[j]=max(f[j],f[j-w[i]]+v[i])
#include<stdio.h> #include<bits/stdc++.h> using namespace std; int n,V,c; int v[100001],l[100001];//v:石头体积 l;所需的体力 int dp[100001]; int main() { scanf("%d %d %d",&V,&n,&c); int sum=0; for(int i=1;i<=n;i++) { scanf("%d %d",&v[i],&l[i]); sum+=v[i]; } if(sum<V)//若石子总体积<V { printf("Impossible"); return 0; } for(int i=1;i<=n;i++) { for(int j=c;j>=l[i];j--) { dp[j]=max(dp[j],dp[j-l[i]]+v[i]); } } for(int i=0;i<=V;i++)//从小到大搜 第一个大于V的直接输出 { if(dp[i]>=V) { printf("%d",c); return 0; } } printf("Impossible"); return 0; }
问题 G: 命运之桥:
此题可以用排序做(高档一点的模拟)
核心思想:两个人相遇转身,相当于交换灵魂后继续走
最大值:最靠近端点两个人各自向对方走,时间较长的那个人的时间
最小值:所有人中走完桥最小值中的最大值
#include<iostream> #include<cmath> #include<algorithm> using namespace std; int l, a[100001]; int main() { int n; scanf("%d",&l); scanf("%d",&n); if (!n) { printf("0 0\n"); return 0; } for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } sort(a + 1, a + 1 + n); //从小到大排序(算最长时间时可能方便一些) int max_time, min_time; for (int i = 1; i <= n; i++) { min_time = max(min(a[i], l + 1 - a[i]), min_time);//最短时间就是所有人中走完桥最小值中的最大值 } max_time = max(l + 1 - a[1], a[n]);//最长时间就是最靠近端点两个人各自向对方走, printf("%d %d", min_time, max_time); return 0; }
问题 H: 恶魔:
一个恶魔能杀掉的最大个数应该为两侧小于它血量的第一个的恶魔能杀掉的个数 + 1,先固定左侧,用单调栈维护一下该侧比当前恶魔更小的第一个位置,右侧同理
#include<bits/stdc++.h> using namespace std; const int N=2e5+10; int n; int a[N],l[N],r[N]; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; stack<int>s; for(int i=1;i<=n;i++)//左边 { while(!s.empty()&&a[s.top()]>a[i]) s.pop(); if(!s.empty()) l[i]=l[s.top()]+1; else l[i]=0; s.push(i); } reverse(a+1,a+1+n);//反转一次,开始做右边 stack<int>s1; for(int i=1;i<=n;i++)//右边 { while(!s1.empty()&&a[s1.top()]>a[i]) s1.pop(); if(!s1.empty()) r[i]=r[s1.top()]+1; else r[i]=0; s1.push(i); } for(int i=1;i<=n;i++) { cout<<l[i]+r[n-i+1]<<" "; } return 0; }
标签:周赛,int,题解,s1,----,++,s2,using,include From: https://www.cnblogs.com/hautacm/p/18598057