202309-1 坐标变换(其一)
#include<iostream>
using namespace std;
int n,m;
int main()
{
int a,b;
cin>>n>>m;
cin>>a>>b;
for(int i=0;i<n-1;i++)
{
int a_1,b_1;
cin>>a_1>>b_1;
a += a_1;
b += b_1;
}
for(int i=0; i<m; i++){
int x,y;
cin>>x>>y;
cout<<x+a<<' '<<y+b<<endl;
}
return 0;
}
202309-2 坐标变换(其二)
- 这道题可以用前缀和、前缀积算出i操作时前面一共旋转多少角度,一共拉升了多少倍,即add[i],mul[i],然后,i->j操作共旋转的角度、倍数 = add[i]-add[j-1]、mul[i]/mul[j-1],最后由公式求得x,y改变后的值
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int n,m;
vector<double> add(N, 0); //旋转角度前缀和
vector<double> mul(N, 1); //拉升倍数前缀积
int main()
{
scanf("%d %d ", &n, &m);
//前缀和从1开始遍历,防止越界
for(int i=1; i<=n; i++){
int a;
double b;
scanf("%d %lf", &a, &b);
if(a == 1){
mul[i] = mul[i-1] * b;
add[i] = add[i-1]; //做拉升操作时,也要给旋转操作等于上一个的值,不然add[i]=0
}
else{
mul[i] = mul[i-1];
add[i] = add[i-1] + b;
}
}
//用来调试
/*cout<<endl;
for(int i=1; i<=n; i++){
cout<<add[i]<<' '<<mul[i]<<endl;
}
cout<<endl;*/
for(int i=1; i<=m; i++){
int i_1, j;
double x, y,ans_x = 0,ans_y = 0;
scanf("%d %d %lf %lf", &i_1, &j, &x, &y);
//求差分
double add_dif = add[j] - add[i_1 - 1];
double mul_dif = mul[j]/mul[i_1 - 1];
ans_x = (x*cos(add_dif) - y*sin(add_dif))*mul_dif;
ans_y = (x*sin(add_dif) + y*cos(add_dif))*mul_dif;
printf("%.3lf %.3lf\n", ans_x, ans_y);
}
return 0;
}
202305-1 重复局面
#include <iostream>
#include <unordered_map>
using namespace std;
int main(){
int n;
char st[64];
unordered_map<string, int> up;
cin>>n;
for(int i=0; i<n; i++){
for(int j=0; j<64; j++){
cin>>st[j];
}
if(up.count(st))up[st]++;
else up[st] = 1;
cout<<up[st]<<endl;
}
return 0;
}
202305-2 矩阵运算
思路:先求n_1 = KT×V, 在求q * n_1
#include <iostream>
#include <unordered_map>
using namespace std;
typedef long long LL;
const int N = 10010, D = 25;
LL n_1[N][D], n_2[N][D];
int q[N][D], k_1[N][D], v[N][D], w[N];
int main(){
int n,d;
cin>>n>>d;
for(int i=0; i<n; i++)
for(int j=0; j<d; j++)
cin>>q[i][j];
for(int i=0; i<n; i++)
for(int j=0; j<d; j++)
cin>>k_1[i][j];
for(int i=0; i<n; i++)
for(int j=0; j<d; j++)
cin>>v[i][j];
for(int i=0; i<n; i++) cin>>w[i];
//计算k^T * v = n_1
for(int i=0; i<d; i++)
for(int j=0; j<d; j++)
for(int k=0; k<n; k++)
n_1[i][j] += k_1[k][i] * v[k][j];
//计算 n_1 * q
for(int i=0; i<n; i++)
for(int j=0; j<d; j++){
for(int k=0; k<d; k++)
n_2[i][j] += q[i][k] * n_1[k][j];
n_2[i][j] *= w[i];
}
for(int i=0; i<n; i++){
for(int j=0; j<d; j++)
cout<<n_2[i][j]<<' ';
cout<<endl;
}
return 0;
}
202303-1 田地丈量
#include <iostream>
#include <unordered_map>
using namespace std;
int main(){
int n,a,b;
int x1, x2, y1, y2;
int ans = 0;
cin>>n>>a>>b;
for(int i=0; i<n; i++){
cin>>x1>>y1>>x2>>y2;
if(x2 <= 0 || y2 <= 0){
continue;
}
//相交区域的长宽
int l,r,t,d;
l = max(x1, 0);
d = max(y1, 0);
r = min(x2, a);
t = min(y2, b);
if(((t-d) > 0) && ((r - l)) > 0) ans += (t - d) * (r - l);
}
cout<<ans;
return 0;
}
202303-2 垦田计划
70分思路:
- 从基础耗时最长的区域进行筛选,每次基础耗时减少一天
该方法以m作为参考对象,对m进行减的操作(m的数据范围达到1e9,导致超时) - 采用优先队列作为存储结构,同时存储t和c
#include <iostream>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
priority_queue<PII, vector<PII>, less<PII>> pr_queue;
int main(){
int n,m,k,ans;
cin>>n>>m>>k;
//输入值
for(int i=0; i<n; i++){
int a,b;
cin>>a>>b;
pr_queue.push({a,b});
}
//对每一个最大天数处理
while(m>0){
auto q = pr_queue.top(); //取出当前基础耗时最大的
pr_queue.pop();
//如果最大天数等于k(每块区域的最少开垦天数),说明所有开荒天数都为k,结束循环
if(q.first == k){
pr_queue.push(q);
break;
}
//减少开荒天数一天,并将总资源m减去这一天消耗的资源
q.first -= 1;
m -= q.second;
pr_queue.push(q); //将减少的天数放回原大根堆
}
cout<<pr_queue.top().first<<endl;
return 0;
}
100分思路1:
- 在70分思路的基础上,发现超时的原因:m的数据范围达到了1e9,造成超时
- 可能改进的方法:按照基础耗时的大小进行筛选,一次循环缩减大量资源。而不是像70分代码思路:一次只减少一天的资源量
- 从基础耗时最小值k,最大值mx进行筛选,一次操作可以使m的值大量减少
- 采用二分算法,对所有可能的耗时进行搜索(范围为k~mx),找到最短耗时
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int t[N], c[N];
int n, m, k, mx = 0;
bool judge(int e){
int sum = 0;
//判断总耗时为e时,资源量是否足够 ,总耗时取决于耗时最长的区域
for(int i=1; i<=n; i++){
if(t[i] < e)continue; //基础耗时小于e,不用缩短天数
sum += (t[i] - e) * c[i];
if(sum > m)return false;
}
return true;
}
int main(){
cin>>n>>m>>k;
for(int i=1; i<=n; i++){
cin>>t[i]>>c[i];
mx = max(mx, t[i]);
}
//二分查找
int l = k, r = mx;
while(l < r){
int mid = (l+r)>>1;
if(judge(mid)) r = mid;
else l = mid + 1;
}
cout<<l<<endl;
return 0;
}
100分思路2:
- 70分思路是对每一个区域单独处理,仔细分析后,发现可以对同一基础耗时的区域同时处理
- 单独开一个数组sa用来记录同一基础耗时的区域所需资源总数
- 从mx到k依次处理,每次使得最大基础耗时的区域同时减少一天
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int t[N], c[N];
int sum[N]; //同一基础耗时缩短1天所需的总投入资源量
int main(){
int n,m,k,mx = 0;
cin>>n>>m>>k;
for(int i=1; i<=n; i++){
cin>>t[i]>>c[i];
sum[t[i]] += c[i]; //计算所有t[i]基础耗时缩短1天所需的总投入资源量
mx = max(mx, t[i]); //最大基础耗时
}
int ans = mx; //答案
//
while(m>0){
m -= sum[ans];
if(m<0)break; //资源不够缩短一天 ,直接跳出循环
sum[ans-1] += sum[ans]; //下一天的需要加上上一天的资源总数
//ans的最小值为k
if(ans == k){
break;
}
ans--;
}
cout<<ans;
return 0;
}
202212-1 现值计算
- 不用pow
#include <iostream>
using namespace std;
const int N = 55;
int num[N];
int main(){
int n,o;
double i;
scanf("%d %lf %d", &n, &i, &o);
double d = 1.0, sum = 0;
for(int j=0; j<n; j++){
scanf("%d", &num[j]);
d *= (1+i);
sum += num[j]/d;
}
printf("%.3lf", sum + (double)o);
return 0;
}
- 用pow
#include <iostream>
#include <cmath>
using namespace std;
const int N = 55;
int num[N];
double sum;
int main(){
int n;
double i;
scanf("%d %lf", &n, &i);
for(int j=0; j<=n; j++){
scanf("%d", &num[j]);
sum += num[j] * pow(1+i, -j);
}
printf("%.3lf", sum);
return 0;
}
202212-2 训练计划
- 思路:题目中,且满足依赖科目的编号小于自己,说明求 最早开始时间 可以从第一个开始枚举,后面的项目的最早开始时间 可以根据前面 已经计算得到的最早开始时间 求得。
- 即在最早开始时间的计算中,每一个科目的最早开始时间依赖于它的前继科目;
- 而在最晚开始时间的计算中,由于某科目是被别的科目依赖的,所以计算它的最晚开始时间时要考虑依赖它的科目能否如期完成,所以我们做个标记 mark ,如果最早可以完成,则继续分析最晚开始时间;
- 而将确定每个科目的最晚,需要从最后的科目往前推,因为如果有依赖的科目,需要把依赖该科目的科目所消耗时间算上,如果没有被依赖,那么最晚开始时间 = 最后期限 - 持续时间的时刻 + 1,如果有被依赖,那么最晚开始时间 = 依赖它的科目的最晚开始的时刻最小的科目 - 本身的持续时间的时刻。
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int dep[N], r_dep[N], day[N], earlier[N], latest[N], r_nums[N];
int main(){
int n, m, mark = 1, idx = 0;
cin>>n>>m;
for(int i=1; i<=m; i++){
cin>>dep[i];
if(dep[i] == 0)r_nums[idx++] = i;
else r_dep[dep[i]] = i;
}
for(int i=1; i<=m; i++){
cin>>day[i];
}
for(int i=1; i<=m; i++){
if(dep[i] == 0)earlier[i] = 1;
else earlier[i] = day[dep[i]] + earlier[dep[i]];
//判断最早开始时间下所有项目是否能被完成
if(earlier[i] + day[i] - 1 > n)mark = 0;
}
for(int i=1; i<=m; i++)cout<<earlier[i]<<' ';
if(mark){
cout<<endl;
for(int i=m; i>0; i--){
//被依赖的项目的最晚开始时间 最小哪个,因为必须保证每个依赖的项目都能完成。所以求min
int tem = n;
for(int j=i+1; j<=m; j++){
if(dep[j] == i) //判断i是否有被依赖的项目j
tem = min(tem, latest[j]);
}
//没有被依赖的
if(tem == n){
latest[i] = n - day[i] + 1;
}
else{
latest[i] = tem - day[i];
}
}
for(int i=1; i<=m; i++)cout<<latest[i]<<' ';
}
return 0;
}
202209-1 如此编码
- 自己写的
#include <iostream>
using namespace std;
typedef long long LL;
LL c = 1,num;
int b[25];
int main(){
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++){
int a;
cin>>a;
c *= a;
b[i] = (m%c - num)/(c/a);
num = m%c;
}
for(int i=1; i<=n; i++)cout<<b[i]<<' ';
}
- 后面看别人写,发现每次不用-num,m % c3 = c0×b1+c1×b2+c2×b3, num = c0×b1+c1×b2,
num % c2始终等于0,所以bi = m%ci/c(i-1),代码如下:
#include <iostream>
using namespace std;
typedef long long LL;
LL c = 1;
int b[25];
int main(){
int n,m;
cin>>n>>m;
for(int i=1; i<=n; i++){
int a;
cin>>a;
c *= a;
b[i] = (m%c)/(c/a);
}
for(int i=1; i<=n; i++)cout<<b[i]<<' ';
}
202209-2 何以包邮?
- 思路一:转化为01背包问题(100)
#include <iostream>
#include <algorithm>
using namespace std;
int num[35];
int f[300010];
int main(){
int n,x;
cin>>n>>x;
//总的价格
int sum = 0;
for(int i=0; i<n; i++){
cin>> num[i];
sum += num[i];
}
int m = sum-x;
//01背包模板
for(int i=0; i<n; i++)
for(int j=m; j>num[i]; j--)
f[j] = max(f[j], f[j-num[i]] + num[i]);
cout<<sum-f[m];
}
- 思路二:离散化(100)
#include<iostream>
using namespace std;
const int N = 35, M = 3e5 + 10;//M为最大总价格,30 * 1e4
int n,x;
int price[N];//存储价格
int f[M];//存储离散化后的点,f[i],i为所有价格的组合,f[i]=1,组合存在
int main()
{
cin>>n>>x;
int sum = 0;
for(int i=0;i<n;i++)
{
cin>>price[i];
sum += price[i];
}
//离散化处理
f[0] = 1; //初始化,让f[price[i]] = 1
for(int i=0; i<n; i++){
//为什么要逆序,顺序的话,假如price[0]=2,当sum=2,f[2]=1, 当sum=4,f[4-price[0]]=f[2]=1
//但是此时只有一个价格为2,4是因为重复用了价格2得到的,所以要逆序。
for(int j=sum; j>=price[i]; j--){
//让所有价格的组合都为1
if(f[j-price[i]]){
f[j] = 1;
}
}
}
//找到第一个大于x价格的组合,也就是最小大于x的价格组合
for(int i=x; i<M; i++){
if(f[i]){
cout<<i;
break;
}
}
return 0;
}
- 思路三:暴力(位运算)(70):n只有选他和不选他的情况,即1,0,有2<<n种情况,则遍历所有情况,算出每一种情况的总价格,找到大于x的总价格中最小的一个
#include<iostream>
using namespace std;
const int N = 35, M = 3e5 + 10;//M为最大总价格,30 * 1e4
int n,x;
int price[N];//存储价格
int main()
{
cin>>n>>x;
for(int i=0;i<n;i++)
{
cin>>price[i];
}
int res = M; //答案
//枚举所有情况
for(int i=0; i<(1<<n); i++){
int sum = 0; //每一种情况的总价格
//有n位
for(int j=0; j<n; j++){
//算出第j位,是1就代表有price[j]
if(i>>j&1){
sum += price[j];
}
}
if(sum >= x)res = min(res, sum);
}
cout<<res;
return 0;
}
- 思路四,暴力(dfs)(70)
#include<iostream>
using namespace std;
const int N = 35, M = 3e5 + 10;
int n,x,res = M;
int price[N];//价格
void dfs(int n_1, int sum){
//走完所有价格后,用总价格更新res
if(n_1 == n){
if(sum>x)
res = min(res, sum);
}
else{
//价格可以选择要还是不要
dfs(n_1+1, sum); //不要price[n_1]
dfs(n_1+1, sum+price[n_1]);
}
}
int main()
{
cin>>n>>x;
for(int i=0;i<n;i++)
{
cin>>price[i];
}
dfs(0, 0);
cout<<res;
return 0;
}
202206-1 归一化处理
#include <iostream>
#include <cmath>
using namespace std;
int n;
double num[1010],sum,f[1010];
int main()
{
cin>>n;
for(int i=0; i<n; i++){
cin>>num[i];
sum += num[i];
}
double avg = sum/n; //平均值
double d; //方差
for(int i=0; i<n; i++){
d += pow(num[i]-avg, 2);
}
d /= n;
for(int i=0; i<n; i++){
f[i] = (num[i] - avg) /sqrt(d);
printf("%f\n",f[i]);
}
return 0;
}