“山大地纬杯”第十二届山东省ICPC大学生程序设计竞赛 AHK

Posted karshey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了“山大地纬杯”第十二届山东省ICPC大学生程序设计竞赛 AHK相关的知识,希望对你有一定的参考价值。

原题链接

A-Seventeen-构造


输入:

10

输出:

1+2+3+4+5+6+7+8-9-10

说明:

The following expression are considered right, too.
-10+1+2+3+4+5+6+7+8-9

((-10+1))+2+3+4+5+6+7+8-9

(-10+1)+2+3+4+5+6+7+8-9

题意:给出n,则1-n这n个数如何通过+、-、*、()得到17?

思路:
构造题。
对所有n>=17都能得到17——17+0,其中0很好凑。
对于所有9<=n<17都能得到17——(8+9)+0,其中0也很好凑。
剩下1-7的数枚举一下即可,1-3无法得出17,4-7可以得到17.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fir(i,a,n) for(int i=a;i<=n;i++)
const int N=1e5+10;
int n;
int main()

	cin>>n;
		if(n>=17)
		
			cout<<"17+(1+2-3)*";
			cout<<"(";
			int f=0;
			fir(i,4,16) 
			
				if(f) cout<<"+";
				cout<<i;f++;
			
			fir(i,18,n)
			
				cout<<"+";
				cout<<i;f++;
			
			cout<<")";
		
		else
		
			if(n>=9)
			
				//8+9
				//1+2-3
				cout<<"(8+9)+(1+2-3)*(";
				int f=0;
				fir(i,4,7) 
				
					if(f) cout<<"+";
					cout<<i;f++;
				
				fir(i,10,n)
				
					cout<<"+"<<i;
				
				cout<<")";
			
			else
			
				//<9
				if(n<=3) cout<<-1;
				else if(n==4) cout<<"3*(1+4)+2";
				else if(n==5) cout<<"2*4+1+3+5";
				else if(n==6) cout<<"(4*6-2*5+3)*1";
				else if(n==7) cout<<"6*7-4*5-2-1*3";
				else if(n==8) cout<<"7*8-5*6-4-2*3+1";
			
		
		cout<<endl;
	return 0;

H-Counting-模拟+哈希



输入:

2 2 2 1
1 1
2 2
R
U

输出:

0
1

题意:大小nxm的地图,给出k和t,k是人数,t是时间。
前k行表示第Ki个人的起始位置,后k行给出长度为t的字符串,表示第t时刻此人的走法。有上下左右四个方向。
输出t+1行,表示第Ti秒时有多少对人的编号(i,j)在同一个位置。(i<j)

思路:
模拟题。
坑点是如何存点。显然这里要存的是一对pair<int,int>,用map会TLE,所以要用unordered_map
这里我们可以手动哈希——已知(x,y),且x,y的范围都小于3000,则我们可以令z=10000*x+y,则每一个不同的点会映射到不同的z,这样就可以用不同的z来表示不同的位置,可以用unordered_map<int,int>来存了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fir(i,a,n) for(int i=a;i<=n;i++)
const int N=3000+10;
int n,m,k,t;
int x[N],y[N];//第n个人的位置 
string str[N];

int main()

	cin>>n>>m>>k>>t;
	fir(i,1,k)  scanf("%d%d",&x[i],&y[i]);//初始位置 
	fir(i,1,k) cin>>str[i];
	fir(i,0,t)
	
		if(i)
		
			for(int j=1;j<=k;j++)
			
				char ch=str[j][i-1];
				//g[x[j]][y[j]]--;
				if(ch=='L')  y[j]--;
				else if(ch=='R') y[j]++;
				else if(ch=='U') x[j]--;
				else x[j]++;
			
		
		
		unordered_map<int,int>mp;
		for(int j=1;j<=k;++j) mp[x[j]*10000+y[j]]++;
		int temp=0;
		for(auto u:mp) temp+=(u.second)*(u.second-1)/2;
		cout<<temp<<endl;
	
	return 0;

所以n,m没有任何作用,因为题目保证数据合法…

K-Coins-枚举 或 打表+找规律



输入:

4
0
1
2
3

输出:

both
-1
A
A

题意:
A有四种硬币:2、3、17、19
B有四种硬币:5,7,11,13
有q次询问,每次输入x,问A和B是否能通过硬币组成x?若都能且使用硬币的最小个数都相等,则输出both,若都不能,则输出-1,否则:若A能则输出A,若B能则输出B。

思路1:
很明显的枚举思路

代码来自:这里

#include<bits/stdc++.h>
using namespace std;
int q,x;
int main()

	scanf("%d", &q);
	while(q--)
		scanf("%d",&x);
		if(x==0) puts("both");
		else
			int ans1=0,ans2=0;
			int flag=0;
			for(int i=x/19; i>=0; i--)
				for(int j=(x-i*19)/17; j>=0; j--)
					for(int k=(x-j*17-i*19)/3; k>=0; k--)
						if((x-k*3-j*17-i*19)%2==0)
							flag=1;
							ans1=i+j+k+(x-k*3-j*17-i*19)/2;
							break;
						
					
					if(flag) break;
				
				if(flag) break;
			
			flag=0;
			for(int i=x/13; i>=0; i--)
				for(int j=(x-i*13)/11; j>=0; j--)
					for(int k=(x-j*11-i*13)/7; k>=0; k--)
						if((x-k*7-j*11-i*13)%5==0)
							flag=1;
							ans2=i+j+k+(x-k*7-j*11-i*13)/5;
							break;
						
					
					if(flag) break;
				
				if(flag) break;
			
			if(ans1==0&&ans2==0) puts("-1");
			else if(ans1==0&&ans2!=0) puts("B");
			else if(ans1!=0&&ans2==0) puts("A");
			else if(ans1==ans2) puts("both");
			else if(ans1>ans2) puts("B");
			else puts("A");
			
	
	return 0;

思路2:
这道题显然有规律。因为每次循环里的x范围都是1e9,,且A的硬币17、19很大,2、3很小,是很巧妙的数据,则这里大概率存在O(1)的解,即要——打表找规律
很显然,本题题意:存在使硬币之和为x且硬币数量最小的值,我们可以把它看作完全背包问题,dp[i][j]表示拿了前i种硬币,背包容量刚好为j的最小代价,其中每一种硬币代价都是1,则可以开始打表:

打表代码(只打前1000个):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fir(i,a,n) for(int i=a;i<=n;i++)
const int N=1e3+10;
int dp[N][N],dpp[N][N];//A B
int w[4],v[4],ww[4],vv[4];//A B 
int main()

	//A
	w[1]=2,w[2]=3,w[3]=17,w[4]=19;
	v[1]=1,v[2]=1,v[3]=1,v[4]=1;
	memset(dp,0x3f,sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=4;i++)
		for(int j=0;j<=1000;j++)
		
			dp[i][j]=dp[i-1][j];
			if(j>=w[i])dp[i][j]=min(dp[i][j],dp[i][j-w[i]]+v[i]);
		
	
	//B
	ww[1]=5,ww[2]=7,ww[3]=11,ww[4]=13;
	vv[1]=1,vv[2]=1,vv[3]=1,vv[4]=1;
	memset(dpp,0x3f,sizeof(dpp));
	dpp[0][0]=0;
	for(int i=1;i<=4;i++)
		for(int j=0;j<=1000;j++)
		
			dpp[i][j]=dpp[i-1][j];
			if(j>=ww[i])dpp[i][j]=min(dpp[i][j],dpp[i][j-ww[i]]+vv[i]);
		
	
	//对比
	for(int i=0;i<=1000;i++)
	
		if(dpp[4][i]==0x3f3f3f3f&&dp[4][i]==0x3f3f3f3f) cout<<"-1";
		else 
		
			if(dp[4][i]==0x3f3f3f3f) cout<<"B";
			else if(dpp[4][i]==0x3f3f3f3f) cout<<"A";
			else
			
				if(dp[4][i]<dpp[4][i]) cout<<"A";
				else if(dp[4][i]>dpp[4][i]) cout<<"B";
				else cout<<"both";
			
		 
		cout<<endl;
	 
	return 0;

输出答案很容易发现——100以后的所有答案都是A。则我们可以把答案直接存下来,x>=100时直接输出A,否则读取我们存的答案,
则有代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fir(i,a,n) for(int i=a;i<=n;i++)
const int N=1e5+10;
//-1 
//A 1
//B 2
//both 0
int n;
int ans[101]=0,-1,1,1,1,2,1,2,1,1,2,2,2,2,2,2,2,1,2,1,0,1,0,0,2,0,2,2,0,2,2,2,2,2,1,2,1,0,1,0,1,1,以上是关于“山大地纬杯”第十二届山东省ICPC大学生程序设计竞赛 AHK的主要内容,如果未能解决你的问题,请参考以下文章

湖南省第十二届大学生计算机程序设计竞赛 G Parenthesis

题解:中南大学第十二届大学生程序设计竞赛

北京师范大学第十二届程序设计竞赛

湖南省第十二届大学生计算机程序设计竞赛 A 2016

湖南省第十二届大学生计算机程序设计竞赛---Parenthesis(线段树求区间最值)

湖南省第十二届大学生计算机程序设计竞赛 problem A 2016