2020.03.04【NOIP提高组】模拟B 组12反思

估分:60+100+50+100
实际:100+100+40+80
说实话吧,这一套题……有点水, 但是我没AK,不过挺意外,我竟然Rank.2!!!!不可思议。T1竟然A了。嘿嘿,不过我不是巨佬,还是个蒟蒻。
T1
背包问题
Description
从T组物品中选出一些物品,放入背包中,求剩余空间的最小值。
限制条件:从每组物品中挑选物品必须要选取连续的一段。就是说,如果这组物品共有n个: 物品1、物品2、物品3、…、物品n,那么只能选取物品i、物品i+1、…、物品j,其中1<=i<=j<=n,或者不选。
Input
第一行为两个用空格隔开的正整数v和T。表示背包的空间和物品的组数。接下来有T行,每行先是一个正整数ni,表示这组物品有ni个,然后ni个正整数,表示每个物品的大小。
Output
仅一个数,表示剩余空间的最小值。
Sample Input
100 3
3 7 6 8
2 80 70
4 101 108 103 150
Sample output
6
Data Constraint
60%的数据满足:1 <= ni <= 10
100%的数据满足:1 <= ni <= 100,1<=v<=5000,1<=T<=10
Hint
第1组选6、8,第2组选80,第3组不选。
期望得分:60。实际得分:100
Solution
首先考虑dp暴力,设一个f[i]表示在每一组里面选择一段区间是否能组成i这么多的空间,f是个bool数组。
每一次暴力枚举区间考虑转移。但是这样肯定会TLE。
经过研究我们发现,有许多的状态都是没有用的。所以我们把所有可行的i都丢尽一个数组里,每次在这个数组里面去更新其他答案,若能更新出一个j就把他丢进数组里。
Code

#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
int m,t,n[11],top=1/*记得赋为1*/;
bool f[5001];
ll sum[11][101],d[5001],ans=0;
int main() {
	scanf("%d%d",&m,&t);
	for(int i=1;i<=t;i++) {
		scanf("%d",&n[i]);
		sum[i][0]=0;
		for(int j=1;j<=n[i];j++) {
			int x;
			scanf("%d",&x);
			sum[i][j]=sum[i][j-1]+(ll)x;//前缀和 
		}
	}
	memset(f,0,sizeof(f));
	f[0]=1;
	for(int i=1;i<=t;i++) {
		int v=top;
		for(int j=1;j<=n[i];j++)
			for(int k=0;k<j;k++)
				for(int s=1;s<=v;s++) {
					if(d[s]+sum[i][j]-sum[i][k]<=m) {
						if(f[d[s]+sum[i][j]-sum[i][k]]==0) {
							f[d[s]+sum[i][j]-sum[i][k]]=1;
							d[++top]=d[s]+sum[i][j]-sum[i][k];//更新数组 
							if(d[top]>ans)ans=d[top];//统计最大的空间 
						}
					}
				}
	}
	printf("%d",m-ans);//总空间减去ans 
}

T2:
破碎的路径
Description
比尔去很多地方旅游过。他在旅游的同时留下了很多简短的旅行笔记。笔记的形式是这样的:
出发地 目的地
如下面就是三条合法的note:
SwimmingPool OldTree
BirdsNest Garage
Garage SwimmingPool
在某一次搬家的时候,比尔的笔记本不小心散架了。于是他的笔记的顺序被完全打乱了。他想请你帮个忙,帮他把这些笔记的顺序整理好,先写的笔记在前面。幸运的是,同一个地方比尔至多只去过一次。也就是说,在这些笔记当中,一个地方至多出现两次,一次作为目的地,一次作为出发地。
Input
第一行是一个整数n,表示笔记的条数。N <= 12000。接下来有n行,每行一条笔记。笔记的两个单词的长度都不会超过15,两个单词之间以一个空格分隔。
Output
输出整理好顺序的笔记.
Sample Input
3
SwimmingPool OldTree
BirdsNest Garage
Garage SwimmingPool
Sample output
BirdsNest Garage
Garage SwimmingPool
SwimmingPool OldTree
Data Constraint
对于50%的数据,n <= 1000。
对于100%的数据,n <= 12000。
期望得分:100。实际得分:100
Solution
第一题做的就是它。很容易可以发现整一幅图其实就是一条链,所以用hash或者用trie存储字符串,然后找到入度为0的点从它开始便利,直到便利完所有点。
Code

#include<cstdio>
#include<cstring>
#define mo 100007;
using namespace std;
int n,h[100008],cnt=0,r[120000],us,vs,k,to[120000];
char u[16],v[16],a[120000][16];
bool key;
bool pd(int x,char s[16]) {
	if(strlen(a[x])!=strlen(s))return 1;
	for(int j=0;j<strlen(s);j++)
		if(a[x][j]!=s[j])return 1;
	return 0;
}
void hash(char w[16]) {
	int now=0;
	for(int j=0;j<strlen(w);j++)
		now=(now*10+w[j])%mo;
	while(h[now]&&pd(h[now],w))now=(now+1)%mo;
	if(!h[now]) {
		h[now]=++cnt;
		for(int j=0;j<strlen(w);j++)
			a[cnt][j]=w[j];
	}
	if(key)vs=h[now];
	else us=h[now];
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%s%s",u,v);
		key=0;hash(u);
		key=1;hash(v);
		r[vs]++;
		to[us]=vs;
	}
	for(int i=1;i<=cnt;i++)
		if(!r[i]) {
			k=i;
			break;
		}
	while(k) {
		int to2=to[k];
		if(!to2)return 0;
		printf("%s %s\n",a[k],a[to2]);
		k=to2;
	}
}

T3:
无线网络
Description
有一个由n台计算机组成的无线网络。(n <= 1001),正常情况下,每台计算机都能跟与它距离不超过d的任何计算机通讯(d <= 20000)。地震发生了。所有的计算机都陷入瘫痪。专家们试着一台一台地修复计算机,以恢复整个无线网络。有时在修复的过程中,他们需要测试一下某两台计算机能否通讯(如果他们能通过别的正常的计算机进行通讯,也算他们之间可以通讯,即“能否通讯”可以是间接的)。
你的任务,就是模拟修复网络的过程,并回答“能否通讯”的询问。
Input
第一行两个整数,N和d,N表示计算机的数目,d表示两台计算机直接可直接通讯的最大距离。接下来的N行,每行两个整数Xi,Yi,表示每台计算机的坐标。接下来有许多行,每行都是一个操作(或者是修复操作,或者是询问操作)。
操作的格式如下:
O p (1 <= p <= N) 修复操作,表示修复编号为p的电脑;
S p q (1 <= p, q <= N) 询问操作,询问编号为p和编号为q的电脑能否通讯。
如果一台电脑尚未被修复,则它不能和任何电脑通讯。
Output
对于每个询问操作:如果能够通讯,输出一行SUCCESS;如果无法通讯,输出一行FAIL
Sample Input
4 1
0 1
0 2
0 3
0 4
O 1
O 2
O 4
S 1 4
O 3
S 1 4
Sample output
FAIL
SUCCESS
Data constraint
对于50%的数据,N <= 300, 操作次数 <= 10000;
对于100%的数据,N <= 1001, 操作次数 <= 300000。
期望得分:50。实际得分:40
Solution
特别特别裸的并查集,(但是我没想到,我太弱了QAQ),每修复一个计算机,就把他能到达的点放进同一个集合。对于每一个询问,如果他们处于同一个集合,就是能够通讯,否则FAIL。
Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
const int N = 1e3 + 3;
int n,d,p,q;
int x[N],y[N],bz[N],fa[N];
char ch;
inline int find(int x) {
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline double sqr(double x) {
	return x*x;
}
inline double getdis(double x1, double y1, double x2, double y2) {
	return sqrt(sqr(x1-x2)+sqr(y1-y2));
}
int main() {
	scanf("%d%d",&n,&d);
	for (int i=1; i<=n; i++) {
		scanf("%d%d",x+i,y+i);
		fa[i]=i;
	}
	while (~scanf("%c%d",&ch,&p)) {
		if (ch=='O') {
			if(bz[p])continue;
			bz[p]=1;
			for (int i=1; i<=n; i++)
				if (i!=p&&bz[i]&&getdis(x[p],y[p],x[i],y[i])<=d)
					fa[find(p)] = find(i);
		} else {
			scanf("%d",&q);
			if (find(p)==find(q)&&bz[p]&&bz[q])puts("SUCCESS");
			else puts("FAIL");
		}
	}
	return 0;
}

T4:
牛棚的安排
Description
FJ的N(1<=N<=50,000)头奶牛实在是太难伺候了,她们甚至有自己独特的产奶时段。当然对于某一头奶牛,她每天的产奶时段是固定的,为时间段A…B(1<=A<=B<=1,000,000),包括时间段A和时间段B。显然,FJ必须开发一个调控系统来决定每头奶牛应该被安排到哪个牛棚去挤奶,因为奶牛们显然不希望在挤奶时被其它奶牛看见。
FJ希望你帮他计算一下:
如果要满足奶牛们的要求,并且每天每头奶牛都要被挤过奶,至少需要多少牛棚
Input
第1行: 一个单独的整数N,为奶牛的总数
第2…N+1行: 每行包括2个用空格隔开的正整数,第i+1行的数据描述的是第i头奶牛的产奶时段
Output
第1行: 输出一个整数M,表示最少需要的牛棚数
Sample Input
5
1 10
2 4
3 6
5 8
4 7
Sample Output
4
Hint
以下是一张与本题输出对应的奶牛产奶时刻表:
时间段 1 2 3 4 5 6 7 8 9 10
牛棚 1 c1>>>>>>>>>>>>>>>>>>>>>>>>>>>
牛棚 2 … c2>>>>>> c4>>>>>>>>> … …
牛棚 3 … … c3>>>>>>>>> … … … …
牛棚 4 … … … c5>>>>>>>>> … … …
(ci表示第i头奶牛)
其他需要同样数目的牛棚的合法输出也是正确的。
期望得分:0。实际得分:80
Solution
不难,有N种方法,还有网络流+二分、差分 、线段树。但是我都没有想到,只想到了一个贪心,会TLE,80分。就是每一头牛,都把它丢进当前最后一头牛离它最近的一个牛棚。
对于100%的数据:
经过思考可发现,答案其实就是每一个时刻在产奶的牛的数量的最大值。所以可以用差分来维护或者用线段树。(有兴趣可以去看看其他博客,有二分+网络流的巨佬算法)我是蒟蒻。
Code

#include<cstdio>
using namespace std;
int n,x,y,a[1000002],maxn=0,ans=0;
inline int read() {
	int s=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s;
}
signed int main() {
	freopen("reserve.in","r",stdin);
	freopen("reserve.out","w",stdout);
	n=read();
	for(register int i=1;i<=n;i++) {
		x=read();a[x]++;
		y=read();a[y+1]--;
		if(y>maxn)maxn=y;
	}
	for(register int j=1;j<=maxn;j++) {
		a[j]=a[j]+a[j-1];
		if(a[j]>ans)ans=a[j];
	}
	printf("%d",ans);
}

总结
1、这一次我又败在了时间上,一定要合理分配时间。这一次如果我能在T3T4在多花一些心思,我一定能够AK。这是多年的老问题,CSP-S的赛场上也是这样(唉~~)。
2、程序实现能力不够强,T2调了这么久都没有调出来,一定要稳住心态,才能调好。
3、一定要想好了思路在码,不然就会心绪变乱。这次T3连续换了好多个代码,打了又换,打了又换。
下次加油!这是NOI-Online前的最后一场模拟赛了,祝大家AK!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值