经过第一周算法的训练,更加感受到自己算法方面的薄弱,希望通过几周的分题型训练,可以有所提高。
文章目录
- E题 简单博弈论 [HDU 1846](https://vjudge.net/problem/11241/origin)
- H题 思维,二进制解法 [CodeForces - 1037A ](https://vjudge.net/problem/1845756/origin)
- I 题 模拟 轮船装载 [POJ - 3282](https://vjudge.net/problem/39465/origin)
- K题 好串 模拟 [CodeForces - 1165C](https://vjudge.net/problem/2439094/origin)
- N题 二分 [POJ - 2785](https://vjudge.net/problem/21897/origin)
E题 简单博弈论 HDU 1846
1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个;
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子;
5、 最先取光石子的一方为胜;
输入数据首先包含一个正整数C(C<=100),表示有C组测试数据。
每组测试数据占一行,包含两个整数n和m(1<=n,m<=1000),n和m的含义见题目描述。
Output
如果先走的人能赢,请输出“first”,否则请输出“second”,每个实例的输出占一行。
Sample Input
2
23 2
4 3
Sample Output
first
second
本题是个简单的博弈论题目,此题中只要我们稍做推导就会发现,对每次最多能拿的石子数加一得到一个结果,然后用总数对其取余,如果等于0,则若以最优解法拿石子,后拿的人必赢。
#include<stdio.h>
int main(){
int N;
scanf("%d",&N);
int sum,m;
while(N--){
scanf("%d %d",&sum,&m);
if(sum%(m+1) == 0){
printf("second\n");
}
else{
printf("first\n");
}
}
return 0;
}
H题 思维,二进制解法 CodeForces - 1037A
淘宝有个规定每种面值的红包只能用一次,且红包里的钱必须恰好使用完才可以使用。
而只会掰手指头数数的涵神自然算不清她到底需要买几种不同面值的红包才可以购买到switch,于是她找到了你来帮她解决问题。
Input
输入只有一行,包括一个整数n(1<=n<=10^9) ,代表switch的双十一价格在1-n元不等
Output
输出只有一行,包括一个整数,代表涵神最少需要购买多少无门槛红包才可以万无一失的买到switch。
Examples
Input
6
Output
3
Input
2
Output
2
题目大意是:寻找可以组成 1~n 内任意数(包括n)的最小的一组数的个数
这个题目很简单,只需要用 n 去减二的从0开始的幂次方,直到为零,计算次数就可以了,
但其实还有另一种理解方法就是,用二进制来做:
我们都清楚,十进制的数据都可以转化为二进制,如:6 -> 222 ,2 -> 01,不难看出,其实本题求解的个数就是该数据转化为二进制后的有效位数
所以我们只需要移位就可以了,每次向右移一位。
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
int i = 0,k = 1;
//常规方法
/*while(n > 0){
n -= k;
k *= 2;
i++;
}
*/
//二进制方法
//在测试数据较多,单个数据较大时,效率更高
while(n){
n = n >> 1;
i++;
}
printf("%d\n",i);
}
对于本题,其实更大的作用是给予我们一种解题的新思路,用二进制方法
I 题 模拟 轮船装载 POJ - 3282
Before bridges were common, ferries were used to transport cars across rivers. River ferries, unlike their larger cousins, run on a guide line and are powered by the river’s current. Cars drive onto the ferry from one end, the ferry crosses the river, and the cars exit from the other end of the ferry.
There is an l-meter-long ferry that crosses the river. A car may arrive at either river bank to be transported by the ferry to the opposite bank. The ferry travels continuously back and forth between the banks so long as it is carrying a car or there is at least one car waiting at either bank. Whenever the ferry arrives at one of the banks, it unloads its cargo and loads up cars that are waiting to cross as long as they fit on its deck. The cars are loaded in the order of their arrival; ferry’s deck accommodates only one lane of cars. The ferry is initially on the left bank where it broke and it took quite some time to fix it. In the meantime, lines of cars formed on both banks that await to cross the river.
Input
The first line of input contains c, the number of test cases. Each test case begins with l, m. m lines follow describing the cars that arrive in this order to be transported. Each line gives the length of a car (in centimeters), and the bank at which the car arrives (“left” or “right”).
Output
For each test case, output one line giving the number of times the ferry has to cross the river in order to serve all waiting cars.
Sample Input
4
20 4
380 left
720 left
1340 right
1040 left
15 4
380 left
720 left
1340 right
1040 left
15 4
380 left
720 left
1340 left
1040 left
15 4
380 right
720 right
1340 right
1040 right
Sample Output
3
3
5
6
模拟题,比较经典,故特意列举出来,本题主要是一步一步模拟就行,题解在注释中
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
/* #include<unistd.h> */
int main(){
int N;
scanf("%d",&N);
int left[10000],right[10000];
int i;
int l,m;
while(N--){
scanf("%d %d",&l,&m);
l *= 100;
if(m == 0){
printf("0\n");
continue;
}
char bank[10];
int left_n = 0,right_n = 0,n;
/* printf("\n1\n"); */
for(i = 0;i < m;i++){ //输入并分别存储数据
scanf("%d %s",&n,bank);
if(bank[0] == 'l')
left[left_n++] = n;
else
right[right_n++] = n;
}
/* printf("\n2\n"); */
int len_car;
int times = 0;
for(int i = 0,j = 0;i < left_n || j < right_n;){ //开始模拟
len_car = 0; //装货长度
times++; //去右边
if(i == left_n && j == right_n){ // 货物已经全部运送完毕,不必去右侧
times--;
break;
}
while(i < left_n && len_car + left[i] <= l){ //左侧装货
len_car += left[i];
i++;
}
/* printf("\ni = %d,j = %d,left_n = %d,right_n = %d\n",i,j,left_n,right_n); */
//sleep(1);
len_car = 0; //长度清零
times++; //回到左侧
if(i == left_n && j == right_n) //全到了右边,不必回到左侧
times--;
while(j < right_n && len_car + right[j] <= l){//右侧装货
len_car += right[j];
j++;
}
/* printf("\n4\n"); */
}
printf("%d\n",times);
}
return 0;
}
K题 好串 模拟 CodeForces - 1165C
Let’s call (yet again) a string good if its length is even, and every character in odd position of this string is different from the next character (the first character is different from the second, the third is different from the fourth, and so on). For example, the strings good, string and xyyx are good strings, and the strings bad, aa and aabc are not good. Note that the empty string is considered good.
You are given a string s, you have to delete minimum number of characters from this string so that it becomes good.
Input
The first line contains one integer n (1≤n≤2⋅105) — the number of characters in s.
The second line contains the string s, consisting of exactly n lowercase Latin letters.
Output
In the first line, print one integer k (0≤k≤n) — the minimum number of characters you have to delete from s to make it good.
In the second line, print the resulting string s. If it is empty, you may leave the second line blank, or not print it at all.
Examples
Input
4
good
Output
0
good
Input
4
aabc
Output
2
ab
Input
3
aaa
Output
3
这个题当时怎么都写不对,现在还没找到当时错误的原因,下面是 AC 的代码
#include<stdio.h>
char s[200005],t[200005];
int main()
{
int n,i=0,j=1,k=0;
scanf("%d",&n);
scanf("%s",s);
i=0;
while(j<n)
{
if(s[i]!=s[j]) //奇数与偶数位置的字符相比较
{
//符合条件,进入t
t[k]=s[i];
k++;
t[k]=s[j];
k++;
i=j+1;
j+=2;
}
else
j++;
}
printf("%d\n",n-k);
for(i=0;i<k;i++)
printf("%c",t[i]);
return 0;
}
N题 二分 POJ - 2785
给你四个数列A,B,C,D。从每个数列中各取出一个数,使4个数的和为0.
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待
Input
第一行:n(代表数列中数字的个数) (1≤n≤4000)
第二行:依次输入A,B,C,D(输入一个A再输入一个B)
Output
输出不同组合的个数。
Sample Input
6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45
Sample Output
5
这道题解法其实不是很好想:主要是要先将前两列和后两列分别相加形成两个n*n的数组,然后再进行排序,二分
#include<stdio.h>
#include<algorithm>
#define maxn 4000
using namespace std;
int map1[maxn*maxn];
int map2[maxn*maxn];
int a[maxn],b[maxn],c[maxn],d[maxn];
int main(){
int n,i,j,sum,p;
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
//分别将两列分项相加,变成两个n*n的数组
for(i=0;i<n;i++)
for(j=0;j<n;j++)
map1[i*n+j]=a[i]+b[j];
for(i=0;i<n;i++)
for(j=0;j<n;j++)
map2[i*n+j]=c[i]+d[j];
//排序
sort(map1,map1+n*n);
sort(map2,map2+n*n);
//二分
sum=0;
p=n*n-1;
for(i=0;i<n*n;i++){
while(p>=0&&map1[i]+map2[p]>0)//在 map2 中寻找与 map[i] 相加大于零的数
p--;
if(p<0) //已经越界
break;
//注意:每个数组中,虽然有同样的数,但其实应属于不同种的情况,
//所以这里要记录 map2 中 p 的位置,以便下一轮继续查找
int temp=p;
while(temp>=0&&map1[i]+map2[temp]==0){
sum++;
temp--;
}
}
printf("%d\n",sum);
return 0;
}
第一周的题目大体就是这样了,第二周继续!