高2018级NOIP模拟赛20190715

链接

1.https://www.cnblogs.com/wutongtong3117/p/7673538.html
2.https://blog.csdn.net/DancingZ/article/details/82707974
3.https://www.cnblogs.com/wutongtong3117/p/7673535.html
4.https://www.cnblogs.com/yangqingli/p/4927698.html

T1 足球联赛(soccer.pas/c/cpp)

题目

传送门:mzoj1495
【问题描述】
巴蜀中学新一季的足球联赛开幕了。足球联赛有 n 只球队参赛,每赛季,每只球队要与其他球队各赛两场,主客各一场,赢一场得 3 分,输一场不得分,平局两只队伍各得一分。

英勇无畏的小鸿是机房的主力前锋,她总能在关键时刻踢出一些匪夷所思的妙球。但是很可惜,她过早的燃烧完了她的职业生涯,不过作为一个能够 Burning 的 girl,她的能力不止如此,她还能预测这个赛季所有球队的比赛结果。虽然她能准确预测所有比赛的结果,但是其实她不怎么厉害,Mr.Gao 上数学课时她总是在 sleep,因此她的脑里只有整数没有实数,而且,她只会 10 以内非负整数的加法运算,因此她只有结果却无法知道谁会获得联赛的冠军。

小鸿想给冠军队伍的所有队员一个拥抱,所以她把计算结果的任务交给了你:现在,给你一个 n*n 的矩阵表示比赛情况。第 i 行第 j 列的字母表示在第 i 只队伍在主场迎战第 j 只队伍的比赛情况,W 表示主队赢,L 表示主队输,D 表示平局。现在需要你给出最后能得到小鸿拥抱的队伍编号,如有多支队伍分数最高,按字典序输出编号。

【输入格式】
第一行一个整数 n。
接下来 n 行,每行 n 个字符,表示输赢情况。
第 i 行第 i 列为 - ,因为一只队伍不可能与自己比赛。
【输出格式】
输出得分最高的队伍编号。如有多个在一行中输出,用一个空格分开。

【样例输入输出 1】
soccer.in
3
-WW
W-W
WW-

soccer.out
1 2 3
【样例输入输出 2】
soccer.in
5
-DWWD
L-WLL
DD-WD
DDL-L
DDLL-

soccer.out
1

【数据范围】
对于 40%的数据,满足 N<=20
对于 100%的数据,满足 N<=50

代码

/*******************************
User:Mandy.H.Y
Language:c++
Problem:soccer
Algorithm:
Date:2019.7.15
Scores:
*******************************/
//道阻且长,行则必至 
#include<bits/stdc++.h>

using namespace std;

const int maxn=55;

int n,ans[maxn],mans=0;
char c[maxn][maxn];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("soccer.in","r",stdin);
	freopen("soccer.out","w",stdout);
}

void readdata(){
	read(n);
	for(int i=0;i<n;++i)
		scanf("%s",c[i]);
}

void work(){
	for(int i=0;i<n;++i){
		for(int j=0;j<n;++j){
			if(i==j) continue;
			switch(c[i][j]){
				case 'W':{
					ans[i]+=3;
					break;
				}
				case 'L':{
					ans[j]+=3;
					break;
				}
				default:{
					ans[i]++;
					ans[j]++;
					break;
				}
			}
			mans=max(mans,max(ans[i],ans[j]));
		}
	}
	for(int i=0;i<n;++i){
		if(ans[i]==mans){
			put(i+1);putchar(' ');
		}
	}
}

int main(){
	file();
	readdata();
	work();
	return 0;
}

T2 最短路径(paths.pas/c/cpp)

题目

传送门:mzoj1496
【问题描述】
平面内给出 n 个点,记横坐标最小的点为 A,最大的点为 B,现在小 Y 想要知道在每个点经过一次(A 点两次)的情况下从 A 走到 B,再回到 A 的最短路径。但他是个强迫症患者,他有许多奇奇怪怪的要求与限制条件:
1.从 A 走到 B 时,只能由横坐标小的点走到大的点。
2.由 B 回到 A 时,只能由横坐标大的点走到小的点。
3.有两个特殊点 b1 和 b2, b1 在 0 到 n-1 的路上,b2 在 n-1 到 0 的路上。
请你帮他解决这个问题助他治疗吧!

【输入格式】
第一行三个整数 n,b1,b2,( 0 < b1,b2 < n-1 且 b1 <> b2)。n 表示点数,从 0 到 n-1 编号,b1 和 b2 为两个特殊点的编号。
以下 n 行,每行两个整数 x、y 表示该点的坐标(0 <= x,y <= 2000),从 0 号点顺序给出。
Doctor Gao 为了方便他的治疗,已经将给出的点按 x 增序排好了。
【输出格式】
输出仅一行,即最短路径长度(精确到小数点后面 2 位)
【样例输入输出】
paths.in
5 1 3
1 3
3 4
4 1
7 5
8 3

paths.out
18.18

【样例解释】
最短路径:0->1->4->3->2->0

【数据范围】
20%的数据 n<=20
60%的数据 n<=300
100%的数据 n<=1000
对于所有数据 x,y,b1,b2如题目描述

代码

/*******************************
User:Mandy.H.Y
Language:c++
Problem:paths
Algorithm:DP 
Date:2019.7.15
Scores:
*******************************/
//尽小者大,慎微者著 
#include<bits/stdc++.h>

using namespace std;

const int maxn=1005;

int n,b1,b2;
double dp[maxn][maxn],dis[maxn][maxn];
//与传纸条有点像
//看成两个人走两个不同路线
//dp[第一个人的位置][第二个人的位置]=最短路径
//保证走到 max(i,j)时,前面的点都走到 
//满足最优子问题  ->  DP  
struct Node{
	int x,y;
}node[maxn];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("paths.in","r",stdin);
	freopen("paths.out","w",stdout);
}

void readdata(){
	read(n);read(b1);read(b2);++b1,++b2;
	for(int i=1;i<=n;++i){
		read(node[i].x);read(node[i].y);
	}
}

void init(){
	
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			dp[i][j]=dp[j][i]=2e9;
			if(i==j) continue;
			dis[i][j]=sqrt(((double)node[i].x-node[j].x)*((double)node[i].x-node[j].x)
					 +((double)node[i].y-node[j].y)*((double)node[i].y-node[j].y));
			dis[j][i]=dis[i][j];
		}
	}
}

void work(){
	init();
	dp[1][1]=0;
	dp[1][2]=dis[1][2];
	dp[2][1]=dis[1][2];
	for(int i=1;i<=n;++i){
		if(i==b2) continue;
		for(int j=1;j<=n;++j){
			if(i==j) continue;
			if(j==b1) continue;//有b1与b2的存在,所以i,j不对称 
			if(j<i-1 && i-1!=b2) dp[i][j]=min(dp[i][j],dp[i-1][j]+dis[i-1][i]);
			//由于前面的点必须走到,而j<i-1,所以上一步 i 必须走到 i-1 
			if(j==i-1) {
				for(int k=1;k<j;++k){
					if(k!=b2)//判断是否合法 
					dp[i][j]=min(dp[i][j],dp[k][j]+dis[i][k]);
				}
			}
			
			if(i<j-1 && j-1!=b1) dp[i][j]=min(dp[i][j],dp[i][j-1]+dis[j][j-1]);
			if(j-1==i) {
				for(int k=1;k<i;++k){
					if(k!=b1)
					dp[i][j]=min(dp[i][j],dp[i][k]+dis[j][k]);
				}
			}
		}
	}
	for(int i=1;i<=n-1;++i){
		dp[n][n]=min(dp[n][n],dp[n][i]+dis[i][n]);
		dp[n][n]=min(dp[n][n],dp[i][n]+dis[i][n]);//两个都要考虑,因为不对称; 
	}
	printf("%.2lf",dp[n][n]);
}

int main(){
	file();
	readdata();
	work();
	return 0;
}
/*******************************
User:Mandy.H.Y
Language:c++
Problem:paths
Algorithm:DP 
Date:2019.7.15
Scores:
*******************************/
/*

题解:https://www.cnblogs.com/wutongtong3117/p/7673537.html
用f[i][j]表示第一个点走到i,第二个点(回去的那个点)走到j的最优值。 
为了保证更新时不会更新出f[i][i],而且每个点都会在路径上, 
我们每次用f[i][j]去更新点max(i,j)+1,所以转移方程为:

f[1][1]=0; 
k=max(i,j)+1, 
f[k][j]=min(f[k][j],f[i][j]+dis(i,k)); 
f[i][k]=min(f[i][k],f[i][j]+dis(j,k));

因为i,j使从1到n循环的,所以每个点都可以分配到来路或去路

对于两个特殊点
if (j==n) f[n][n]=min(f[n][n],f[i][n]+dis(i,n)); 
if (i==n) f[n][n]=min(f[n][n],f[n][j]+dis(j,n)); 
//一个节点到头了,只能动另一个节点

if (k!=b1) f[i][k]=min(f[i][k],f[i][j]+dis(j,k)); 
if (k!=b2) f[k][j]=min(f[k][j],f[i][j]+dis(i,k)); 
*/
//尽小者大,慎微者著 
#include<bits/stdc++.h>

using namespace std;

const int maxn=1005;

int n,b1,b2;
double dp[maxn][maxn],dis[maxn][maxn];
//与传纸条有点像
//看成两个人走两个不同路线
//dp[第一个人的位置][第二个人的位置]=最短路径
//保证走到 max(i,j)时,前面的点都走到 
//满足最优子问题  ->  DP  
struct Node{
	int x,y;
}node[maxn];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("paths.in","r",stdin);
	freopen("paths.out","w",stdout);
}

void readdata(){
	read(n);read(b1);read(b2);++b1,++b2;
	for(int i=1;i<=n;++i){
		read(node[i].x);read(node[i].y);
	}
}

void init(){
	
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			dp[i][j]=dp[j][i]=2e9;
			if(i==j) continue;
			dis[i][j]=sqrt(((double)node[i].x-node[j].x)*((double)node[i].x-node[j].x)
					 +((double)node[i].y-node[j].y)*((double)node[i].y-node[j].y));
			dis[j][i]=dis[i][j];
		}
	}
}

void work(){
	init();
	dp[1][1]=0;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			if((i!=1||j!=1)&&i==j) continue;
			int k=max(i,j)+1;
			if(k==n+1){
				if(i==n) dp[n][n]=min(dp[n][n],dp[i][j]+dis[j][n]);
				if(j==n) dp[n][n]=min(dp[n][n],dp[i][j]+dis[i][n]);
			} else{
				if(k!=b2) dp[k][j]=min(dp[k][j],dp[i][j]+dis[i][k]);//回的时候不走b1 
				if(k!=b1) dp[i][k]=min(dp[i][k],dp[i][j]+dis[k][j]);//去的时候不走b2
			}
		}
	}
	printf("%.2lf",dp[n][n]);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

T3 阿 Q 的停车场(park.pas/c/cpp)

传送门:mzoj1497

问题描述
刚拿到驾照的 KJ 总喜欢开着车到处兜风,玩完了再把车停到阿 Q 的停车场里,虽然 她对自己停车的水平很有信心,但她还是不放心其他人的停车水平,尤其是 Kelukin。于是, 她每次都把自己的爱车停在距离其它车最远的一个车位。

KJ 觉得自己这样的策略非常科 学,于是她开始想:在一个停车场中有一排车位,从左到右编号为 1 到n,初始时全部是 空的。有若干汽车,进出停车场共 m 次。

对于每辆进入停车场的汽车,会选择与其它车距 离最小值最大的一个车位,若有多个符合条件,选择最左边一个。KJ 想着想着就睡着了, 在她一旁的 Kelukin 想帮她完成这个心愿,但是他又非常的懒,不愿意自己动手,于是就把 这个问题就留给了你:在 KJ 理想的阿 Q 的停车场中,给你车辆进出的操作序列,依次输 出每辆车的车位编号。

输入格式
第一行,两个整数 n 和 m,表示停车场大小和操作数;
接下来 m 行,每行两个整数 F 和 x F 是 1 表示编号为 x 的车进停车场; F 是 2 表示编号为 x 的车出停车场;
保证操作合法,即: 出停车场的车一定目前仍在停车场里; 停车场内的车不会超过 n;

输出格式
对于所有操作 1,输出一个整数,表示该车车位的编号

样例输入
7 11
1 15
1 123123
1 3
1 5
2 123123
2 15
1 21
2 3
1 6
1 7
1 8

样例输出
1
7
4
2
7
4
1
3

提示
【数据范围】
对 30%的数据 n<=1000 ,m<=1000 对
60%的数据 n<=200000,m<=2000
对 100%的数据 n,m<=200000,
车的编号小于等于 10^6

代码

/*******************************
User:Mandy.H.Y
Language:c++
Problem:park
Algorithm:
Date:2019.7.15
Scores:
*******************************/

//若网在纲,有条不紊 


//solution:https://www.cnblogs.com/wutongtong3117/p/7673535.html 不过这篇题解的代码有点问题 
//一般情况下,只要找出该区间内没有停车的最远的区间, 
//区间长度>>1就是下一辆要停的车与其他车相距的最远距离 

//要维护四个量 
//分别是l,r,mid,pos
//l表示在当前结点线段树所在区间,最左边的车停的位置 
//同理,r表示做右边的车所停的位置 
//mid表示在这个小区间[x,y]中的紧邻的两辆车的最长距离除以2后的值 
//pos表示取得mid值是所在的紧邻的两辆车的中间位置,也就是在[x,y]中的答案值
#include<bits/stdc++.h>

using namespace std;

const int maxn=2e5+5;

int n,m,mid,mdis=2e9;
int car[1000005];

struct Node{
	int l,r,mid,pos;
}tree[maxn<<2]; 

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("park.in","r",stdin);
//	freopen("park.out","w",stdout);
}

void readdata(){
	read(n);read(m);
}

void update(int k){
	if(!k) return;
	int lson=k<<1;
	int rson=k<<1|1;
	
	if(tree[lson].l) tree[k].l=tree[lson].l;
	else tree[k].l=tree[rson].l;
	if(tree[rson].r) tree[k].r=tree[rson].r;
	else tree[k].r=tree[lson].r;//如果等于零,说明这个区间是空的,就以另一个区间为准 
	
	tree[k].mid=tree[lson].mid;tree[k].pos=tree[lson].pos;
	
	if(tree[lson].r&&tree[rson].l){//这里不能等于零,因为必须要有边界 
		int len=(tree[rson].l-tree[lson].r)>>1;	
		//由于与之相比较的是 tree[1].x-1,n-tree[1].y所以直接 tree[rc].x-tree[lc].y
		//由于是两个区间之间的空位,故 tree[rc].x-tree[lc].y
		if(len>tree[k].mid){
			tree[k].mid=len;
			tree[k].pos=(tree[rson].l+tree[lson].r)>>1;
		}
	}
	
	if(tree[rson].mid>tree[k].mid){
		tree[k].mid=tree[rson].mid;
		tree[k].pos=tree[rson].pos;
	}//左中右,保证字典序最小
	return;
}

void add(int l,int r,int k,int pos,int val){
	if(l==r&&l==pos){
		if(val){
			tree[k].l=pos;tree[k].r=pos;
			tree[k].mid=0;tree[k].pos=0;   //节点上有车了,当然就没有mid和p值了 
		}else{
			tree[k].l=0;tree[k].r=0;
			tree[k].mid=0;tree[k].pos=0; 
		}
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) add(l,mid,k<<1,pos,val);
	if(pos>mid) add(mid+1,r,k<<1|1,pos,val);
	update(k);
}

void work(){
	mid=1;
	mdis=0; 
	while(m--){
		int opt,u;
		read(opt);read(u);
		if(opt==1){
			if(!tree[1].l){
				puts("1");
				car[u]=1;
			}else{
				int len=-1;
				
				if(tree[1].l-1>len){
					len=tree[1].l-1;
					car[u]=1;
				}//第一个车位 
				
				if(tree[1].mid>len){
					len=tree[1].mid;
					car[u]=tree[1].pos;
				}
				
				if(n-tree[1].r>len){
					len=n-tree[1].r;
					car[u]=n;
				}//最后一个车位 
				
				put(car[u]);putchar('\n');
			}
			add(1,n,1,car[u],1);//加车 
		}else add(1,n,1,car[u],0);
	}
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值