链接:https://www.nowcoder.com/acm/contest/91/A
来源:牛客网
A.Wasserstein Distance
题目描述
左图为第一堆泥土的初始形态,右图为第二堆泥土的初始形态,颜色代表了一种可行的移动方案,使得第一堆泥土的形态变成第二堆泥土的形态
输入描述:
输入测试组数T,每组测试数据,第一行输入n,1<=n<=100000,紧接着输入两行,每行n个整数,前一行为a1
, a2
,…,an
,后一行为b1
,b2
,…,bn
.其中0<=ai
,bi
<=100000,1<=i<=n,数据保证
输出描述:
对于每组数据,输出一行,将a土堆的形态变成b土堆的形态所需要花费的最小体力
输入
2 3 0 0 9 0 2 7 3 1 7 6 6 6 2
输出
2 9
最小体力,可能题目数据不怎么好,暴力可以直接过,我是先相减的,然后从头遍历到后面。
直到找完。
1 #include <iostream> 2 #include <cstring> 3 #define mem(a) memset(a,0,sizeof(a)) 4 #define ll long long int 5 #define N 100020 6 using namespace std; 7 int a[N],b[N]; 8 int t; 9 int main(){ 10 cin>>t; 11 while(t--){ 12 mem(a); 13 mem(b); 14 int n; 15 cin>>n; 16 for(int i=0;i<n;i++) 17 cin>>a[i]; 18 for(int i=0;i<n;i++){ 19 cin>>b[i]; 20 a[i]=a[i]-b[i]; 21 } 22 ll sum = 0; 23 for(int i=0;i<n;i++){ 24 if(a[i]<0){ 25 for(int j=i+1;j<n;j++){ 26 if(a[j]>0){ 27 if(a[j]>=abs(a[i])){ 28 sum+=abs(a[i])*(j-i); 29 a[j]+=a[i]; 30 a[i]=0; 31 }else{ 32 sum+=a[j]*(j-i); 33 a[i]+=a[j]; 34 a[j]=0; 35 } 36 } 37 if(a[i]==0) 38 break; 39 } 40 }else if(a[i]>0){ 41 for(int j=i+1;j<n;j++){ 42 if(a[j]<0){ 43 if(abs(a[j])>=a[i]){ 44 sum+=a[i]*(j-i); 45 a[j]+=a[i]; 46 a[i]=0; 47 }else{ 48 sum+=abs(a[j])*(j-i); 49 a[i]+=a[j]; 50 a[j]=0; 51 } 52 } 53 if(a[i]==0) 54 break; 55 } 56 } 57 } 58 cout<<sum<<endl; 59 } 60 return 0; 61 }
链接:https://www.nowcoder.com/acm/contest/91/E
来源:牛客网
题目描述
小Y买了很多苹果,但他很贪吃,过了几天一下就吃剩一只了。每一天小Y会数出自己的苹果个数X,如果X是偶数,他就会吃掉只苹果;如果X是奇数,他就会吃掉
只苹果。
你知道现在苹果只剩下一只,并且小Y是在N天前买的苹果,现在小Y想知道在那天买了多少苹果。当然,可能性不止一种,你只需要求出他买的苹果数量有多少种可能。
输入描述:
输入数据只有一个整数N,表示小Y在N天前买了苹果。
输出描述:
输出一个整数,表示可能的数量种数。
输入
1
输出
2
说明
样例中小Y在一天前买了苹果,因此他只可能买了2个或者3个苹果,共2种情况。
这个就是最简单的一道,就不说了。
1 #include <iostream> 2 #define ll long long int 3 using namespace std; 4 5 int main(){ 6 int n; 7 cin>>n; 8 ll sum =1; 9 while(n--){ 10 sum*=2; 11 } 12 cout<<sum<<endl; 13 return 0; 14 }
链接:https://www.nowcoder.com/acm/contest/91/F
来源:牛客网
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
小Y在研究数字的时候,发现了一个神奇的等式方程,他屈指算了一下有很多正整数x满足这个等式,比如1和2,现在问题来了,他想知道从小到大第N个满足这个等式的正整数,请你用程序帮他计算一下。
(表示按位异或运算)
输入描述:
第一行是一个正整数,表示查询次数。
接着有T行,每行有一个正整数,表示小Y的查询。
输出描述:
对于每一个查询N,输出第N个满足题中等式的正整数,并换行。
输入
4 1 2 3 10
输出
1 2 4 18
这题确实不错,开始准备暴力,没看清题意,
只后发现那是不存在的!!!tan 90.
这里理清思路,仔细找会发现就是只要没有1和1紧靠就可以了。
然后就可以dp了,再加上前缀和就行了。
想起来有点复杂,但是想通了就很简单了。
1 #include <iostream> 2 #include <cmath> 3 #include <queue> 4 #include <algorithm> 5 #define ll long long int 6 using namespace std; 7 8 ll dp[65]; 9 int n; 10 11 int main(){ 12 cin>>n; 13 dp[1]=1; 14 dp[0]=0; 15 for(int i=2;i<=60;i++){ 16 dp[i] = dp[i-1]+dp[i-2]; 17 } 18 for(int i=1;i<=60;i++){ 19 dp[i]+=dp[i-1]; 20 } 21 for(int i=0;i<=60;i++){ 22 dp[i]++; 23 // cout<<dp[i]<<" ** "; 24 } 25 // cout<<endl; 26 while(n--){ 27 ll m; 28 cin>>m; 29 ll cnt = 0; 30 while(m){ 31 for(int i=1;i<=60;i++){ 32 if(dp[i]>m&&dp[i-1]<=m){ 33 cnt+=(1ll<<(i-1)); 34 m-=dp[i-1]; 35 break; 36 } 37 } 38 } 39 cout<<cnt<<endl; 40 } 41 return 0; 42 }
链接:https://www.nowcoder.com/acm/contest/91/I
来源:牛客网
题目描述
换句话说,给定一个十进制下最多105位的数字,请你求出和这个数字的差的绝对值最小的二数,若答案不唯一,输出最小的那个。
输入描述:
1 ≤ T ≤ 100, 1 ≤ n ≤ 10100000
− 1, T组数据的数字的十进制表示长度总和不超过1000000
输出描述:
每行一个整数 m 第 i 行表示第 i 个数所对应的“最邻近二数”
输入
5 42 11 1 2018 13751
输出
42 8 0 2020 8888
这道题ennnnnnnnnn。。怎么说呢,想到了思路,但是比赛的时候没写出来,之后补上来的,
不想写大数,就直接搬了一份大数的源代码过来了。
首先思路是字符串输入,
找到第一个奇数,没有就直接输出。
有的话就看当前位置是否是第一位,然后分别做两种选择,一个是向上取,一个是向下取。
注意特殊情况,比如:第一位为1,和第一位为9,还有就是只有一位数为1.
剩下的就仔细点就可以了。
1 #include <bits/stdc++.h> 2 #define mem(a) memset(a,0,sizeof(a)) 3 using namespace std; 4 5 int Subtraction(char num1[], char num2[], int sum[]) 6 { 7 int i, j, len, blag; 8 char *temp; 9 int n2[1000000] = {0}; 10 int len1 = strlen(num1); // 计算数组num1的长度,即大数的位数 11 int len2 = strlen(num2); // 计算数组num2的长度,即大数的位数 12 blag = 0; 13 if(len1 < len2) 14 { 15 blag = 1; // 标记结果为负数 16 temp = num1; 17 num1 = num2; 18 num2 = temp; 19 len = len1; 20 len1 = len2; 21 len2 = len; 22 } 23 else if(len1 ==len2) // 如果被减数的位数等于减数的位数 24 { 25 for(i = 0; i < len1; i++) 26 { 27 if(num1[i] == num2[i]) 28 continue; 29 if(num1[i] > num2[i]) 30 { 31 blag = 0; // 标记结果为正数 32 break; 33 } 34 else 35 { 36 blag = 1; // 标记结果为负数 37 temp = num1; 38 num1 = num2; 39 num2 = temp; 40 break; 41 } 42 } 43 } 44 len = len1>len2 ? len1 : len2; 45 for (i = len1-1, j = 0; i >= 0; i--, j++) 46 sum[j] = num1[i] - '0'; 47 for (i = len2-1, j = 0; i >= 0; i--, j++) 48 n2[j] = num2[i] - '0'; 49 for (i = 0; i <= len; i++) 50 { 51 sum[i] = sum[i] - n2[i]; // 两个数从低位开始相减 52 if (sum[i] < 0) // 判断是否有借位 53 { // 借位 54 sum[i] += 10; 55 sum[i+1]--; 56 } 57 } 58 for (i = len1-1; i>=0 && sum[i] == 0; i--) 59 ; 60 len = i+1; 61 if(blag==1) 62 { 63 sum[len] = -1; // 在高位添加一个-1表示负数 64 len++; 65 } 66 return len; // 返回结果的位数 67 } 68 char s[100000]={0}; 69 int sum[100000]={0},sum1[100000]={0}; 70 char s1[100000]={0},s2[100000]={0}; 71 int main(){ 72 ios::sync_with_stdio(false); 73 cin.tie(0); 74 int t; 75 cin>>t; 76 while(t--){ 77 mem(s); 78 mem(sum); 79 mem(sum1); 80 mem(s1); 81 mem(s2); 82 cin>>s; 83 bool prime = true; 84 int p=0,q=0; 85 for(int i=0;i<strlen(s);i++){ 86 if(!prime){ 87 s1[p++]='8'; 88 s2[q++]='0'; 89 continue; 90 } 91 if((s[i]-'0')%2==1&&prime){ 92 if(i==0){ 93 if(s[i]!='1'){ 94 s1[p++]=s[i]-1; 95 } 96 if(s[i]=='9'){ 97 s2[q++]='2'; 98 s2[q++]='0'; 99 }else{ 100 s2[q++]=s[i]+1; 101 } 102 }else{ 103 s1[p++]=s[i]-1; 104 105 if(s[i]=='9'){ 106 s2[q-1]+=1; 107 q++; 108 s2[q++]='0'; 109 }else{ 110 s2[q++]=s[i]+1; 111 } 112 } 113 prime = false; 114 }else{ 115 s1[p++]=s[i]; 116 s2[q++]=s[i]; 117 } 118 } 119 if(strlen(s1)==0){ 120 s1[p++]='0'; 121 } 122 int slen = Subtraction(s2, s, sum); 123 int slen1 = Subtraction(s, s1, sum1); 124 if(slen>slen1){ 125 cout<<s1<<endl; 126 }else if(slen<slen1){ 127 cout<<s2<<endl; 128 }else{ 129 bool flag = true; 130 for(int i=slen-1;i>=0;i--){ 131 if(sum[i]>sum1[i]){ 132 cout<<s1<<endl; 133 flag = false; 134 break; 135 }else if(sum[i]<sum1[i]){ 136 cout<<s2<<endl; 137 flag = false; 138 break; 139 } 140 } 141 if(flag){ 142 cout<<s1<<endl; 143 } 144 } 145 } 146 return 0; 147 }
链接:https://www.nowcoder.com/acm/contest/91/L
来源:牛客网
题目描述
输入描述:
第一行为两个整数 n, K, 以空格分隔,第二行为 n 个整数,表示 a[1] ∼ a[n],1 ≤ n ≤ 105
, 1 ≤ a[i] ≤ 109
, 1 ≤ nK ≤ 107
输出描述:
输出一个整数表示最长子序列的长度 m
输入
7 5 10 3 4 2 2 9 8
输出
6
这题也是可以用前缀和来算的,然后遍历,尽量优化一下,避免超时。
1 #include <iostream> 2 #include <cstring> 3 #define ll long long int 4 #define N 1000000 5 using namespace std; 6 7 int a[N]; 8 int n,m; 9 int main(){ 10 cin>>n>>m; 11 for(int i=1;i<=n;i++){ 12 cin>>a[i]; 13 a[i]%=m; 14 } 15 for(int i=2;i<=n;i++){ 16 a[i] = a[i]+a[i-1]; 17 } 18 int sum = 0; 19 for(int i=0;i<=n;i++){ 20 for(int j=i+1;j<=n;j++){ 21 if(j-i<sum){ 22 j=i+sum-1; 23 } 24 int aa = a[j]-a[i]; 25 if(aa%m==0){ 26 sum = max(sum,j-i); 27 } 28 } 29 } 30 cout<<sum<<endl; 31 32 return 0; 33 }
赛完之后看到题解说,其实是后台数据没出好,当它为字串也可以过。
所以又重新学了一下,滚动数组加压缩dp:
代码如下:
题解:
为求最大子序列,就是找到最长。
开始先将输入的初始化,就是先对K求模,然后我们来看一下滚动数组就是将空间简化,
当我当前的状态只与我前一次的数据有关时,我只需要保留前一状态的数据就可以了,就
这样减少空间复杂度,然后就是下面的状态压缩了。
dp[last][n] 指的时余数为n时的子序列的最长长度。
所以下面两个状态压缩方程就是根据它的上一次的状态来取决的,dp[1-last][n].
只有当我前一个状态为为非零的状态时,才好进行累加操作,否则就会是错的。
dp[last][(j+a[i])%m] = max(dp[1-last][(j+a[i])%m],dp[1-last][j]+1);
否则,我们就将前一个状态传给当前状态。
dp[last][(j+a[i])%m] = dp[1-last][(j+a[i])%m];
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 int dp[2][10000005]; 6 int a[110000]; 7 int n,m; 8 9 int main(){ 10 cin>>n>>m; 11 for(int i=0;i<n;i++){ 12 cin>>a[i]; 13 a[i]%=m; 14 } 15 memset(dp,0,sizeof(0)); 16 dp[0][a[0]] = 1; 17 int last = 0;//滚动标记 18 for(int i=1;i<n;i++){ 19 last = 1-last; 20 for(int j=0;j<m;j++){ 21 if(dp[1-last][j]){ 22 dp[last][(j+a[i])%m] = max(dp[1-last][(j+a[i])%m],dp[1-last][j]+1); 23 }else{ 24 dp[last][(j+a[i])%m] = dp[1-last][(j+a[i])%m]; 25 } 26 } 27 } 28 cout<<dp[last][0]<<endl; 29 return 0; 30 }