ccf csp备忘录

​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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值