复盘8.3训练:Nordic Collegiate Programming Contest 2020
Posted karshey
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了复盘8.3训练:Nordic Collegiate Programming Contest 2020相关的知识,希望对你有一定的参考价值。
A-Array of Discord
题意:
输入一排数字升序排列,是否可以改变其中一个数字的任意一位数字使之不是有顺序的?
正确代码请见官方代码或最后的代码
一开始的思路:
- 若存在两个相同的数,则第一个加一后输出
- 若是每个数的位数递增,则输出“impossible”
- 若存在位数相同的数,讲前面的数字变最大或后面的数字变最小(如1111,1112变为9111,1112 即为变最大)
WA了,因为分类太复杂而不全,代码也无可避免的复杂。
而且:
题目中的sort并未指从小到大或从大到小,即sort是从小到大或从大到小都可以的。
只要存在小,大,小或大,小,大这样的排列,就是不sort的,符合题意。
正确思路:把一个数字变为最大后整体判断一遍是否符合标准,都不符合就把第二数字变为最小判断。
注意:用函数实现不同的功能,方便纠错,且代码简洁清晰。 思路不要太复杂。
官方答案:
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
const int N=2;
int n, m;
LL w[N];
void out()
{
for (int i = 1; i <= n; ++i) {
printf("%lld ", w[i]);
}
}
LL to_number(string s)
{
LL res = 0;
for (int i = 0; i < s.size(); ++i) {
res = res * 10 + s[i] - '0';
}
return res;
}
void to_big(int u)
{
string s = to_string(w[u]);
for (int i = 0; i < s.size(); ++i) {
if (s[i] != '9') {
s[i] = '9';
break;
}
}
w[u] = to_number(s);
return;
}
void to_small(int u)
{
string s = to_string(w[u]);
if (s.size() == 1) {
w[u] = 0;
return;
}
else {
if (s[0] != '1') {
s[0] = '1';
}
else {
for (int i = 1; i < s.size(); ++i) {
if (s[i] != '0') {
s[i] = 0;
break;
}
}
}
}
w[u] = to_number(s);
}
bool is_sort()
{
int flag = 1;
for (int i = 1; i <= n - 1; ++i) {//全都一样也是sort的
if (w[i] != w[i + 1]) {
flag = 0;
break;
}
}
if (flag) return false;
flag = 1;
for (int i = 1; i <= n - 1; ++i) {
if (w[i] > w[i + 1]) {
flag = 0;
break;
}
}
if (flag) return false;
return true;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &w[i]);
}
for (int i = 1; i <= n; ++i) {
LL t = w[i];
to_big(i);
if (t != w[i] && is_sort()) {
out();
return 0;
}
w[i] = t;
to_small(i);
if (t != w[i] && is_sort()) {
out();
return 0;
}
w[i] = t;
}
puts("impossible");
return 0;
}
自己按照这个思路写,只过了86%,然后转变了思路,试了试字符串的做法:
- 字符串长度相同的有可能可改
- 改完之后判断
代码如下(通过97.73%)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=115;
string a[N];
int n;
string to_big(string str)
{
for(int i=0;str[i];i++)
{
if(str[i]!='9')
{
str[i]='9';break;
}
}
return str;
}
string to_small(string str)
{
if(str.size()==1) str='0';
else
{
if(str[0]!='1') str[0]='1';
else
{
for(int i=1;str[i];i++)
{
if(str[i]!='0')
{
str[i]='0';break;
}
}
}
}
return str;
}
bool is_sort()
{
for(int i=0;i<n-1;i++)
{
if(a[i].size()==a[i+1].size())
{
for(int j=0;a[i][j];j++)
{
if(a[i][j]>a[i+1][j])//出现大的
{
flag=0;break;
}
}
}
else if(a[i].size()>a[i+1].size())
{
flag=1;break;
}
}
if(flag) return 0;
return 1;
}
void out()
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
//big
for(int i=0;i<n-1;i++)
{
if(a[i].size()==a[i+1].size())
{
string t=a[i];//存a[i]
a[i]=to_big(a[i]);
if(is_sort())
{
out();return 0;
}
a[i]=t;
}
}
//small
for(int i=1;i<n;i++)
{
if(a[i].size()==a[i-1].size())
{
string t=a[i];
a[i]=to_small(a[i]);
if(is_sort())
{
out();return 0;
}
a[i]=t;
}
}
cout<<"impossible"<<endl;
}
但它还是太复杂了。对于本题,只要字符串符合可改的条件,改了是一定符合题意的,即:字符串a,b,若它们:
- 长度为1,且a不为0,b不为9,则它们可改且符合题意
- 长度不为1,a第一位不为1,b第一位不为9,可改且符合题意
因此可以简化代码:
终于过了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=105;
int n;
string a[N];
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int q=0,w=0;
for(int i=1;i<n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(a[i].size()==a[j].size())
{
if(a[i].size()==1&&a[i][0]=='0'&&a[j][0]=='9') continue;
if(a[i].size()>1&&a[i][0]=='1'&&a[j][0]=='9') continue;
//可以变化
q=i;w=j;break;
}
}
if(q!=0) break;
}
if(q!=0)
{
if((a[q][0]!='1'||a[q].size()==1)&&a[q][0]!='0')//后面的可以减
{
a[w][0]=a[q][0]-1;
}
else a[q][0]=a[w][0]+1;
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
}
else cout<<"impossible"<<endl;
return 0;
}
小总结:
- 多写函数,分功能
- 1015可以用string或long long
- 理解题意sort,出现拐点才算(所以n==2的永远impossible)
C-Coin Stacks
原题
一个水题。
题意:n堆硬币,每堆a[i]个,能否一次选两堆各拿一个,使之拿完?
如果能就输出yes和拿出的顺序。
思路:
- 总和为奇数的no
- 最大的比剩下的所有之和要大的no
- 其他yes 输出顺序为:每次输出最大的两个的编号,再排序
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=70;
struct node
{
int num,val;
};
int cmp(node a,node b)
{
if(a.val!=b.val) return a.val>b.val;//从大到小
else return a.num<b.num;
}
node a[N];
int n;
int main()
{
ios::sync_with_stdio(false);
cin>>n;
int sum=0,maxn=-1;
for(int i=1;i<=n;i++)
{
cin>>a[i].val;
a[i].num=i;
sum+=a[i].val;
maxn=max(maxn,a[i].val);
}
if(sum%2==1)
{
cout<<"no";
return 0;
}
if(maxn>sum-maxn)
{
cout<<"no";
return 0;
}
cout<<"yes"<<endl;
while(sum)
{
sort(a+1,a+n+1,cmp);
cout<<a[1].num<<" "<<a[2].num<<endl;
a[1].val--;
a[2].val--;
sum-=2;
}
return 0;
}
D-Dams in distress
题意:n个结点,m为冲垮0号大坝需要的雨水。
n行输入,输入dd,cc,uu,分别代表连接的结点编号,大坝满的雨水量,现有的雨水量。
求想要冲垮0号大坝的最少雨水。
动态规划。
一个结点需要的雨水量可能是:
- 冲破自己的大坝需要的雨水:cc-uu
- 冲破下面的大坝需要的雨水:res[dd]-uu
选择大的(不然没法转移)。
ans是最小的res。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int res[N];
//DP
int main()
{
ios::sync_with_stdio(false);
int n,ans;cin>>n>>ans;
res[0]=ans;
for(int i=1;i<=n;i++)
{
int dd,cc,uu;cin>>dd>>cc>>uu;
res[i]=max(res[dd]-uu,cc-uu);//第一个是足够冲垮下游的,第二个是足够冲垮自己的,肯定要冲垮一个大的,否则能冲垮自己却不能冲下游/能冲下游不能冲自己
ans=min(ans,res[i]);
}
cout<<ans;
}
G-Gig Combinatorics
题意:n个数,按照数字的顺序存在几个1开头,3结尾,中间是2的组合?
动态规划。
但我把它理解为排列组合问题。
输入的数据可能为1,2,3:
- 1:是开始,每输入一个1,dp1++
- 2:是中间,每输入一个2,dp2+=dp1+dp2.其中加的dp1指的是新加的2与原先的1的所有组合,加的dp2指的是新加的2与原先所有12的组合
- 3:是结束,每输入一个3,都有个数为dp2的组合可以结束了。
关于MOD:
每加一次都要%MOD一次,不然无法通过。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
int dp1,dp2,以上是关于复盘8.3训练:Nordic Collegiate Programming Contest 2020的主要内容,如果未能解决你的问题,请参考以下文章
Nordic Collegiate Programming Contest 2015? B. Bell Ringing
Nordic Collegiate Programming Contest 2015? G. Goblin Garden Guards
(寒假GYM开黑)2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)
Gym .101933 Nordic Collegiate Programming Contest (NCPC 2018) (寒假gym自训第四场)
2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)
2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) A. Altruistic Amphibians DP(示