【复盘】8.3训练:Nordic Collegiate Programming Contest 2020

地址

A-Array of Discord

A

题意:
输入一排数字升序排列,是否可以改变其中一个数字的任意一位数字使之不是有顺序的?

正确代码请见官方代码或最后的代码

一开始的思路:

  • 若存在两个相同的数,则第一个加一后输出
  • 若是每个数的位数递增,则输出“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;
}

小总结:

  1. 多写函数,分功能
  2. 1015可以用string或long long
  3. 理解题意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,dp3;
ll MOD=1e9+7;
int main()
{
	ios::sync_with_stdio(false);
	int n;cin>>n;
	for(int i=0;i<n;i++)
	{
		int a;cin>>a;
		if(a==1) 
		{
			dp1++;dp1%=MOD;
		}
		else if(a==2) 
		{
			dp2+=dp1+dp2;
			dp2%=MOD;
		}
		else 
		{
			dp3+=dp2;
			dp3%=MOD;
		}
	}
	
	cout<<dp3;
}

L-Methodic Multiplication

原题
大水题,推演一下,计数即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=105;
int main()
{
	string a,b;
	cin>>a>>b;
	if(a=="0"||b=="0")//0
	{
		cout<<0;
	}
	else
	{
		int aa=0,bb=0;
		for(int i=0;a[i];i++)
		{
			if(a[i]=='S') aa++;
		}
		for(int i=0;b[i];i++)
		{
			if(b[i]=='S') bb++;
		}
		int sum=aa*bb;
		for(int i=0;i<sum;i++)
		{
			cout<<"S(";
		}
		cout<<"0";
		for(int i=0;i<sum;i++)
		{
			cout<<")";
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值