埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛

链接:https://www.nowcoder.com/acm/contest/91/A
来源:牛客网


A.Wasserstein Distance

题目描述

最近对抗生成网络(GAN)很火,其中有一种变体WGAN,引入了一种新的距离来提高生成图片的质量。这个距离就是Wasserstein距离,又名铲土距离。
这个问题可以描述如下:


有两堆泥土,每一堆有n个位置,标号从1~n。第一堆泥土的第i个位置有ai克泥土,第二堆泥土的第i个位置有bi克泥土。小埃可以在第一堆泥土中任意移挪动泥土,具体地从第i个位置移动k克泥土到第j个位置,但是会消耗的体力。小埃的最终目的是通过在第一堆中挪动泥土,使得第一堆泥土最终的形态和第二堆相同,也就是ai=bi (1<=i<=n), 但是要求所花费的体力最小

左图为第一堆泥土的初始形态,右图为第二堆泥土的初始形态,颜色代表了一种可行的移动方案,使得第一堆泥土的形态变成第二堆泥土的形态


输入描述:

输入测试组数T,每组测试数据,第一行输入n,1<=n<=100000,紧接着输入两行,每行n个整数,前一行为a
1
, a
2
,…,a
n
,后一行为b
1
,b
2
,…,b
n
.其中0<=a
i
,b
i
<=100000,1<=i<=n,数据保证 

输出描述:

对于每组数据,输出一行,将a土堆的形态变成b土堆的形态所需要花费的最小体力
示例1

输入

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
来源:牛客网

E.小Y吃苹果

题目描述

小Y买了很多苹果,但他很贪吃,过了几天一下就吃剩一只了。每一天小Y会数出自己的苹果个数X,如果X是偶数,他就会吃掉只苹果;如果X是奇数,他就会吃掉只苹果。

你知道现在苹果只剩下一只,并且小Y是在N天前买的苹果,现在小Y想知道在那天买了多少苹果。当然,可能性不止一种,你只需要求出他买的苹果数量有多少种可能。

输入描述:

输入数据只有一个整数N,表示小Y在N天前买了苹果。

输出描述:

输出一个整数,表示可能的数量种数。
示例1

输入

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
来源:牛客网

1 + 2 = 3?
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述

小Y在研究数字的时候,发现了一个神奇的等式方程,他屈指算了一下有很多正整数x满足这个等式,比如1和2,现在问题来了,他想知道从小到大第N个满足这个等式的正整数,请你用程序帮他计算一下。

(表示按位异或运算)

输入描述:

第一行是一个正整数,表示查询次数。

接着有T行,每行有一个正整数,表示小Y的查询。

输出描述:

对于每一个查询N,输出第N个满足题中等式的正整数,并换行。
示例1

输入

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
来源:牛客网

I.二数

题目描述

我们把十进制下每一位都是偶数的数字叫做“二数”。
小埃表示自己很聪明,最近他不仅能够从小数到大:2,3,4,5....,也学会了从大数到小:100,99,98...,他想知道从一个数开始数最少的数就得到一个二数。但是聪明的小森已经偷偷在心里算好了小埃会数到哪个二数,请你求出他要数到哪个数吧。
换句话说,给定一个十进制下最多105位的数字,请你求出和这个数字的差的绝对值最小的二数,若答案不唯一,输出最小的那个。
也就是说,给定数字n,求出m,使得abs(n-m)最小且m[i] mod 2 = 0

输入描述:

1 ≤ T ≤ 100, 1 ≤ n ≤ 10
100000
 − 1, T组数据的数字的十进制表示长度总和不超过1000000

输出描述:

每行一个整数 m 第 i 行表示第 i 个数所对应的“最邻近二数”
示例1

输入

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
来源:牛客网

L.K序列

题目描述

给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”。现在要你 对数组 a 求出最长的子序列的长度,满足这个序列是 K 序列。 

输入描述:

第一行为两个整数 n, K, 以空格分隔,第二行为 n 个整数,表示 a[1] ∼ a[n],1 ≤ n ≤ 10
5
 , 1 ≤ a[i] ≤ 10
9
 , 1 ≤ nK ≤ 10
7

输出描述:

输出一个整数表示最长子序列的长度 m
示例1

输入

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 }

 

posted @ 2018-04-15 20:34  #忘乎所以#  阅读(391)  评论(0编辑  收藏  举报