训练赛题解

A、最少拦截系统
A题是一个最长递增子序列的题,因为题问最少需要多少拦截系统,对于每个拦截系统来说,它们所能包含的就是一组非递增子序列,所以对于两组中的数字来说,一定能找到后一组中的一个数字大于前一组一个数字,所以根据这个结论可得分成的组数就是所给数中的最长递增子序列,每个数都属于一个系统。这是一个dp的题,可以用dp来解,不过也有针对最长递增子序列的更便捷的算法就是如下,利用的原理就是上面讲的原理。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<math.h>
#include<vector>
#define ull unsigned long long 
using namespace std;

int main(){
	int n;
	while(cin>>n){
		vector<int>v;
		int a;
		for(int i=0;i<n;i++){
			cin>>a;
			if(v.empty()||a>v[v.size()-1]) v.push_back(a);
			else{
				int k=lower_bound(v.begin(),v.end(),a)-v.begin();
				v[k]=a;
			}
		}
		cout<<v.size()<<endl;
	}
}

B、ACboy needs your help again!
B题就是一个用stl队列的模拟题,这个题过的还挺多的,挺好

#include<iostream>
#include<stdio.h>
#include<string>
#include<algorithm> 
#include<vector>
#include<stack>
#include<queue>
typedef long long ll;
using namespace std;

int main(){
	int t;
	cin>>t;
	while(t--){
		int n;
		string a;
		cin>>n>>a;
		if(a=="FIFO"){
			queue<int>q;
			string b;
			int c;
			while(n--){
				cin>>b;
				if(b=="IN"){
					cin>>c;
					q.push(c);
				}else{
					if(q.empty()){
						cout<<"None"<<endl;
						continue;
					}
					cout<<q.front()<<endl;
					q.pop();
				}
			}
		}else{
			stack<int>s;
			string b;
			int c;
			while(n--){
				cin>>b;
				if(b=="IN"){
					cin>>c;
					s.push(c);
				}else{
					if(s.empty()){
						cout<<"None"<<endl;
						continue;
					}					
					cout<<s.top()<<endl;
					s.pop();
				}
			}
		}
	} 
} 

C、青蛙的约会
C题是一个扩展欧几里得算法的题,题中说让两个青蛙见面,设两个青蛙的步差为a,纬线长度是l,两个蛙的距离为c,且纬线首位相连,可以一直绕,所以则若两个青蛙可以见面则 ax = c + ly 存在解,所以可得要ax + ly = c 有解,所以就要用扩展欧几里得算法来求x和y,再倍增,最后再取余,取余的原因是 原式=(x1+k×l/g)×a+y×l= c = x1×a+k×l×a/g+y×l=c = x1×a+(k×a/g+y)×l=c,这样就可以得到x的最小值。(题解字母含义和题中不一定一样,这个题要注意负数情况)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<map>
using namespace std;

long long ex_gcd(long long a,long long b,long long &x,long long &y){
	if(!b){
		x=1;
		y=0;
		return a;
	}
	long long d=ex_gcd(b,a%b,y,x);
	y-=x*(a/b);
	return d;
}

int main(){
	long long x,y,m,n,l;
	cin>>x>>y>>m>>n>>l;
	long long c=y-x;	
	long long g=ex_gcd(m-n,l,x,y);
	if(c%g||!(m-n)){
		cout<<"Impossible"<<endl;
		return 0;
	}
	x*=c/g;
	long long t=l/g;
	if(t<0) t=-t;
	x=(x%t+t)%t;
	cout<<x<<endl;
}

D、Inversion
D题题意是要让数组变有序交换k次后最少还要交换几次,对于一个无序数组两两交换变成有序许需要交换的次数就是整个数组的逆序数,逆序数求法很多,这个题我是用归并算法求逆序,当然大家有其他方法也都可以。原理就是归并在每次合并时若前比后大,则逆序应该是前面数组的后面所有数个数,因为是从前往后合并的,且一组内是有序的,前比后大,那前的后也比后大,就这样求逆序就可以了。

#include <iostream> 
#include <algorithm>
#include <vector> 
#include <string.h>
using namespace std;
const int maxn=1e5+7;
int a[maxn],b[maxn];
long long ans;
void un(int l,int mid,int r){
	int t=l,k=mid+1,u=l;
	while(u<=mid&&k<=r){
		if(a[u]>a[k]){
			b[t++]=a[k++];
			ans+=(long long)(mid-u+1);
		}
		else b[t++]=a[u++];
	}
	while(u<=mid) b[t++]=a[u++];
	while(k<=r) b[t++]=a[k++];
	for(int i=l;i<=r;i++){
		a[i]=b[i];
	}
}
void sortt(int l,int r){
	if(l==r) return;
	int mid=(l+r)>>1;
	sortt(l,mid);
	sortt(mid+1,r);
	un(l,mid,r);
}

int main(){
	int n,k;
	while(~scanf("%d%d",&n,&k)){
		ans=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		sortt(1,n);
		if(k>=ans) printf("0\n");
		else printf("%lld\n",ans-(long long)k);
	}
} 

E、Monkey Party
E题是一个区间dp的题,emm,过的不多···。这个题题意是两个猴子介绍认识就要把它们认识的都介绍一边认识,可以把它简化为两堆合并成一堆的代价就要把两堆的和相加起来,算所有合成一堆的最小代价。这是一个区间dp的板子题,三层for循环,最外层遍历区间长度,中间层遍历区间起点,最下面一层遍历区间中的合并点,这样来求小区间合并成大区间的最小代价,最后得到最大区间的最小合并代价。(第三层用了区间dp的优化,在dp[i][j-1]的最小分割点和dp[i+1][j]的最小分割点之间找dp[i][j]最小分割点)
原理:罗勇军老师的四边形不等式优化讲解

#include<iostream>
#include<cstdio>
#include<algorithm> 
#include<cstring>
#include<string>
#include<vector>
using namespace std;
int a[2010];
int dp[2010][2010];
int s[2010][2010];
int inf=0x3f3f3f3f;

int main(){
	int n;
	while(cin>>n){
		for(int i=1;i<=n;i++){
			cin>>a[i];
			a[i+n]=a[i];
		}
		memset(dp,inf,sizeof(dp));
		for(int i=1;i<=n*2;i++) a[i]+=a[i-1];
		for(int i=1;i<=n*2;i++){
			dp[i][i]=0;
			s[i][i]=i;
		}
		for(int len=1;len<n;len++){
			for(int i=1;i<=n;i++){
				int j=i+len;
				for(int k=s[i][j-1];k<=s[i+1][j];k++){
					if(dp[i][k]+dp[k+1][j]+a[j]-a[i-1]<dp[i][j]){
						dp[i][j]=dp[i][k]+dp[k+1][j]+a[j]-a[i-1];
						s[i][j]=k;
					}
				}
			}
		}
		int m=1e9;
		for(int i=1;i<=n;i++){
			m=min(m,dp[i][i+n-1]);
		}
		cout<<m<<endl;
	}               
}

F、Radar Installation
F题是一个贪心题,但是也要拐个弯的贪,这个题的题意就是问最少需要多少个点雷达覆盖所有的岛屿,说实话,要是从雷达的角度来考虑这个问题,要画半圆,这很麻烦很难想,所有要从逆向思维来思考这个问题,从岛屿的角度来思考,要覆盖这个岛屿,能放雷达的区域有多大。最后把那些重复的区域合并起来,让雷达尽量放在最多重复的区域,这样可以保证雷达放的最少。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <string>
#include <queue>
#define ll long long
using namespace std;
struct node{
	double x,y;
}e[1010],t[1010];
bool cmp(node a,node b){
	return a.x<b.x;
}

int main(){
	int n,d;
	int cnt=1;
	while(~scanf("%d%d",&n,&d)&&n){
		int f=0;
		for(int i=0;i<n;i++){
			scanf("%lf%lf",&e[i].x,&e[i].y);
		}
		for(int i=0;i<n;i++){
			if(e[i].y>d){
				cout<<"Case "<<cnt++<<": "<<-1<<endl;
				f=1;
				break;
			}
		}
		if(f) continue;
		double temp;
		for(int i=0;i<n;i++){
			temp=e[i].x;
			double s=sqrt(d*d-e[i].y*e[i].y);
			t[i].x=temp-s;
			t[i].y=temp+s;
		}
		sort(t,t+n,cmp);
		int ans=0;
		for(int i=0;i<n;i++){
			ans++;
			double xx=t[i].y;
			i++;
			for(;i<n;i++){
				if(t[i].x<=xx){
					if(t[i].y<xx) xx=t[i].y;
				}else{
					i--;
					break;
				}
			}
		}
		cout<<"Case "<<cnt++<<": "<<ans<<endl;
	}
}

G、棋盘问题
G题题意是,这是个中文题就不说题意了,这题用的是dfs算法,比较经典的dfs棋盘问题的变形,很暴力的一道题,本来想用来签到的···,我相信大家不会的话看了代码就一定会懂的。就找能放棋子的地方来遍历就行了。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
int inf=0x3f3f3f3f;
int n,k;
char mp[10][10];
bool vis[10];
int ans;
void dfs(int x,int cnt){
	if(cnt==k){
		ans++;
		return;
	}
	if(x>=n){
		return;
	}
	for(int i=0;i<n;i++){
		if(!vis[i]&&mp[x][i]=='#'){
			vis[i]=1;
			dfs(x+1,cnt+1);
			vis[i]=0;
		}
	}
	dfs(x+1,cnt);
}

int main()
{
	while(~scanf("%d%d",&n,&k)){
		if(n==k&&k==-1){
			break;
		}
		ans=0;
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				cin>>mp[i][j];
			}
		}
		dfs(0,0);
		cout<<ans<<endl;
	}
}
  • 15
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值