• CSDN竞赛4期题解


    总结

    csdn的竞赛,与其他OJ的周赛相比,有些不太好的体验。举个例子,T1只是想分段收个电费,150度以下的怎么收费,151度以上的怎么收费,按照表述151度电就只收150度的电费,因为没说150-151度之间怎么收费了;T2虽然题目没啥毛病,但是给句子中单词逆序,本来需要做些操作的,cpp给定的模板直接将每个单词存入vector了,要做的就是给vector倒着取下数,没啥意义。T3一开始给的唯一一个用例是3 3,结果是10,给3拆分成3个不小于1的整数,有10种拆法,后面刷新修正为1,用例出错不说什么了。如果给4分为两个整数,那么1 3和3 1算一种还是两种题目也没有表述清楚;T4就是像求个最小的修改数量是多少,问的是通过修改最小的数字变成美丽数列,很容易误解为只能改最小的数字。希望后面的竞赛能改掉这些不足的地方吧。

    下面题解代码为赛后书写,所以还不能保证能够通过评测,如果有问题,还望各位大佬帮忙指正。

    题目列表

    1.小玉家的电费

    题目描述

    夏天到了,各家各户的用电量都增加了许多,相应的电费也交的更多了。小玉家今天收到了一份电费通知单。小玉看到上
    面写:据闽价电[2006]27号规定,月用电量在150千瓦时及以下部分按每千瓦时0.4463元执行,月用电量在151~400
    千瓦时的部分按每千瓦时0.4663元执行,月用电量在401千瓦时及以上部分按每千瓦时0.5663元执行;小玉想自己验证一
    下,电费通知单上应交电费的数目到底是否正确呢。请编写一个程序,已知用电总计,根据电价规定,计算出应交的电费
    应该是多少。

    分析

    首先忽略题目不恰当的表述,重新理解为150以下电费多少,150-400电费多少,400以上电费多少,然后分类讨论下即可。

    代码

    #include 
    #include 
    using namespace std;
    int main() {
    	float n;
    	cin>>n;
    	float res = 0;
    	if(n <= 150)	res += n * 0.4463;
    	else {
    		res += 150 * 0.4463;
    		if(n <= 400)	res += (n - 150) * 0.4663;
    		else {
    			res += 250 * 0.4663 + (n - 400) * 0.5663;
    		}
    	}
    	printf("%.1f\n",res);
    	return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.单词逆序

    题目描述

    对于一个字符串,请设计一个算法,只在字符串的单词间做逆序调整,也就是说,字符串由一些由空格分隔的部分组成,
    你需要将这些部分逆序。 给定一个原字符串A,请返回逆序后的字符串。例,输入”I am a boy!“输出”boy! a am I“

    分析

    题目输入模板已经帮我们分割了单词,逆序输出即可。

    代码

    #include 
    #include 
    #include 
    #include 
    std::vector<std::string> solution(std::vector<std::string>& vec){
    	std::vector<std::string> result;
    	int n = vec.size();
    	for(int i = n - 1;i >= 0;i--)	result.push_back(vec[i]);
    	return result;
    }
    int main() {
    	std::vector<std::string> vec;
    	std::string line_0, token_0;
    	getline(std::cin >> std::ws,line_0);
    	std::stringstream tokens_0(line_0);
    	while(std::getline(tokens_0, token_0, ' ')){
    		vec.push_back((token_0));
    	}
    	std::vector<std::string> result = solution(vec);
    	for(auto it=result.begin();it!=result.end();++it){
    		std::cout<<*it<<" ";
    	}
    	std::cout<<std::endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.小Q整数分割

    题目描述

    小Q决定把一个整数n,分割为k个整数。 每个整数必须大于等于1。 小Q有多少方案。

    分析

    举个例子,如果将4分割为2个正整数,1 3和3 1算不同的方案数的话,那么只需要考虑简单的组合数问题。也就是等价于有一个长度为n的线段,要将这条线段切成k段,就需要在线段上找k - 1个切点,一共有C(n - 1,k - 1)种切法,这个组合数就是答案。

    但是本题实际的含义将1 3和3 1视为不同的方案,这个就有点难度了。可以理解为在1到n中找k个正整数,使得这k个正整数之和等于n的方案数。然后问题就变成了方案里面1选了多少个,2选了多少个,…。就可以使用背包问题的思想来解决本题了。

    状态表示:f[i][j][t]表示1到i中选了t个整数,且这t个整数的和是j的方案数。t个整数的和可以理解为背包问题中的体积,t相当于附加的限制条件。

    状态边界:f[i][0][0] = 1表示前i个整数中一个不选的方案数。

    状态转移:需要考虑i这个整数选了几次,选的次数可以是0, 1, 2, …, r次,那么状态转移方程为f[i][j][t] = sum(f[i-1][j-i*r][t-r])。

    复杂度分析:枚举i、j、r需要三重循环,而枚举i出现的次数还需要一重循环,本题的数据规模是100,O(n4)的复杂度就有点危险了,考虑使用背包问题类似的优化手段。

    考虑f[2][8][4]这个状态转移的过程
    2这个数字可以出现0到4次
    f[2][8][4] = f[1][8][4] + f[1][6][3] + f[1][4][2] + f[1][2][1] + f[1][0][0]
    再来看f[2][6][3]状态的转移过程
    f[2][6][3] =              f[1][6][3] + f[1][4][2] + f[1][2][1] + f[1][0][0]
    显然,f[2][8][4] = f[2][6][3] + f[1][8][4]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    根据上面的例子我们可以得到优化后的状态转移方程:f[i][j][t] = f[i][j-i][t-1] + f[i-1][j][t]。这样一来时间复杂度降到了立方级别,106的计算次数是完全可以接受的。由于每个状态只用到了左边以及上一层相同的状态,所以可以使用滚动数组实现,去掉第一维状态。

    代码

    #include 
    using namespace std;
    const int N = 105, MOD = 1e9 + 7;
    int n,k;
    int f[N][N];
    int main() {
    	cin>>n>>k;
    	f[0][0] = 1;
    	for(int i = 1;i <= n;i++) {
    		for(int j = i;j <= n;j++) {
    			for(int t = 1;t <= k;t++) {
    				f[j][t] = (f[j][t] + f[j - i][t - 1]) % MOD;
    			}
    		}
    	}
    	cout<<f[n][k]<<endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4.新型美丽数列

    题目描述

    定义美丽数列A: 1. 数列中相邻的数越是靠内相对大小加一,a[2]=a[1]+1,a[n-2]=a[n-1]+1… 2. 距离边缘距离相等的
    数的大小相等:a[0] = a[n-1],a[1] = a[n-2]… 通过修改最小的数字使得给定数列变成美丽数列。 修改后的值必须仍是正
    整数。
    输入描述:
    第一行输入整数n。(1<=n<=1000)表示数列的大小。
    第二行输入n个整数。
    输出描述:
    输出最小修改。
    输入样例:
    3
    1 1 1
    输出样例:
    1

    分析

    比赛时候想的是DFS和DP的思路,后面没时间就没作答了。比赛之后又看了下,分析出题目的隐藏条件,就很容易解决了。

    首先,明确题意,题目表述不太清楚,我理解为数列有奇数个元素时,像1 2 3 2 1,除了中间数只有一个,其他数都是由内而外逐渐减去一的;数列有偶数个元素时,像1 2 2 1这种中间两个数是相等的,其他数由内而外减去一。然后给定若干个数列,判断要多少次操作才能将给定的数列转化为美丽数列。(PS:这么简单的表述,题目表述不清像是直接机器从英文题面翻译过来的。)

    解题的关键在于一个性质,表面看是要修改很多元素才能变为美丽数列,实际只要确定了中间的数字,其他数字也就确定了。举个例子:

    1 4 3 2 5

    设中间的元素3改变为t,那么最终的数列就是t - 2,t - 1, t,t - 1,t - 2,来看下每个数字的改变量,分别是t - 3, t - 5, t - 3 - 1, t - 3, t - 7,现在希望改变的元素数量最少,也就是希望改变量数组中0的数量最多,而t是变量,意味着只要让改变量数组中出现最多的元素是0,那么就可以使得改变的元素数量最少,最后发现t - 3出现的次数最多,是3次,所以有三个位置元素不用改变,因此t取3最合适。

    如果数列有偶数个元素呢,那么只需要设中间两个数改变为t即可。

    具体在实现时,并不需要设变量t,只需要对最中间的数保持不变,由内而外每个数字一次加上1,2,3,…即可,比如1 4 3 2 5,处理后得到3 5 3 3 7,然后出现最多的数字就是3,所以最中间的数字取3,最后不等于3的数有2个,就是需要改变的数量。所以本题相当于给原数组做了下归一化之后求个哈希。

    代码

    #include 
    #include 
    #include 
    using namespace std;
    const int N = 1005;
    int n,a[N];
    unordered_map<int,int> m;
    int main() {
    	cin>>n;
    	for(int i = 0;i < n;i++)	cin>>a[i];
    	int t = (n - 1) / 2;
    	if(n & 1) {
    		int k = 1;
    		while(t - k >= 0) {
    			a[t-k] += k;
    			a[t+k] += k;
    			k++;
    		}
    	}
    	else {
    		int k = 1;
    		while(t - k >= 0) {
    			a[t-k] += k;
    			a[t+k+1] += k;
    			k++;
    		}
    	}
    	for(int i = 0;i < n;i++)	m[a[i]]++;
    	int s = 0;
    	for(auto x : m) {
    		s = max(s,x.second);
    	}
    	cout<<n - s<<endl;
    	return 0;
    } 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
  • 相关阅读:
    OpenCV--图像的运算
    Springboot企业差旅报销系统_5h38k计算机毕业设计-课程设计-期末作业-毕设程序代做
    redis的持久化
    小程序自定义组件以及组件传值的简单总结
    Makefile学习
    【无标题】
    Oracle事务
    生命周期详解
    【BLE CORE】二、GAP(Generic Access Profile)
    设计模式-享元模式
  • 原文地址:https://blog.csdn.net/qq_30277239/article/details/126491471