2019.04.20【NOIP提高组】模拟 B 组 观察题目+堆(?)+最大匹配+贪心、DP

21 篇文章 0 订阅
11 篇文章 0 订阅

0 压缩后缀数组

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
据传是水题
当f[i]=i时,原数组s的i位置一定是偶数,那么s[i]=a[对应的位置]*2
否则s[i]=s[f[i]]-1

#include <cstdio>

using namespace std;

int n;
int c[20004],a[20004],tot;
int s[20004];

int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&c[i]);
	for (int i=1;i<=n/2;i++) 
		scanf("%d",&a[i]);
	for (int i=1;i<=n;i++)
	if (c[i]==i) s[i]=a[++tot]*2;
	for (int i=1;i<=n;i++)
	if (c[i]!=i)s[i]=s[c[i]]-1;
	for (int i=1;i<=n;i++)
		printf("%d ",s[i]);
}

1 新年礼物

Windbreaker计划送一些项链给他的朋友们作为新年礼物。为了表示诚意,他决定自己制作全部的项链。他购买了若干种珍珠,每种珍珠都有特定的颜色。他要制作的项链都是M-完美的,也就是每条项链都是恰好由M种珍珠组成的。Windbreaker想知道他最多能送出多少条项链。给定每种珍珠的数目,你要回答的是Windbreaker最多可以制作多少条M-完美项链。

我,应该是水过去的
就每次找出最大的m个数,都减去5(太小超时,太大错,所以说是水过去的啦),放进堆里;重复以上步骤知道堆内大于0的不足m个

#include <cstdio> 
#include <queue>
#include <cstring>

using namespace std;

priority_queue<int> q;
int n,m,ans,d;
priority_queue<int> a;

int main(){
	scanf("%d",&n);
	while (n!=0){
		while (!q.empty()) q.pop();
		for (int i=1;i<=n;i++){
			int x;
			scanf("%d",&x);
			q.push(x);
		}
		scanf("%d",&m);
		ans=0;
		while (!a.empty()) a.pop();
		while (q.size()>=m){
			for (int i=1;i<=m;i++){
				a.push(-q.top());
				q.pop();
			}
			if (!q.empty()) d=(-a.top()-q.top()+5); else d=-a.top();
			if (d<0-a.top()) ans+=d; else d=-a.top(),ans+=d;
			for (int i=1;i<=m;i++){
				if (-a.top()-d>0) q.push(-a.top()-d);
				a.pop();
			}
		}
		printf("%d\n",ans);
		scanf("%d",&n);
	}
}

2 聚会

Tzdin想组织一个圣诞晚会。N位女士和M位男士(M>=N)会被邀请参加这个聚会。在聚会的开始,Tzdin会派发一些写着某位男士信息的卡片给每位女士;每位女士都会收到若干张这种卡片。然后每位女士可以从她收到的卡片里挑选一位男士作为她的伴侣。我们可以认为经过Tzdin的引导,每位女士都一定可以挑选到一位男士作为他的伴侣,而每位男士最多成为1位女士的伴侣。Tzdin想知道的是,有哪些男士,无论女士们怎么选择,最终都一定会拥有伴侣。

想到是匈牙利算法了呀,方法错了呗:P
应该先找出最大匹配,然后对于每个女士,让她不和在最大匹配中的那位男士匹配,如果此时她无法匹配,那么这个男士满足一定会有伴侣
毕竟最大匹配是 O ( n 3 ) O(n^3) O(n3)

#include <cstdio>
#include <cstring>

using namespace std;

const int N=1001;
int n,m;
int a[1003][1003];
bool b[1003],f[1003];
int s[1003],cover[1003];
int s2[1003];

bool dfs(int w){
	for (int i=1;i<=a[w][0];i++)
	if ((b[a[w][i]])&&(!cover[a[w][i]])){
		int l=s[a[w][i]];
		s[a[w][i]]=w;
		cover[a[w][i]]=1;
		if ((l==0)||(dfs(l))) return 1;
		s[a[w][i]]=l;		
	}
	return 0;
}

bool find(){
	for (int i=1;i<=n;i++){
		memset(cover,0,sizeof cover);
		if (!dfs(i)) return 0; 
	}
	return 1;
}

int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++){
		scanf("%d",&a[i][0]);
		for (int j=1;j<=a[i][0];j++)
			scanf("%d",&a[i][j]);
	}
	memset(b,1,sizeof b);
	memset(f,0,sizeof f);
	find();
	for (int i=1;i<=m;i++){
		b[i]=0;
		memset(cover,0,sizeof cover);
		memcpy(s2,s,sizeof s2);
		if ((s[i]!=0)&&(!dfs(s[i]))) f[i]=1;
		b[i]=1;
		memcpy(s,s2,sizeof s);
	}
	for (int i=1;i<=m;i++)
	if (f[i])
		printf("%d\n",i);
} 

3 辽哥游戏

张辽是一个长发飘飘的非常聪明的男孩,人人都称他为“辽哥”。辽哥喜欢玩一个有趣的电脑游戏。这个游戏开始的时候有n个碉堡,每个碉堡拥有一个防御值和一个附加值。玩家拥有一个初始的攻击力。如果玩家破坏了一个碉堡,则他能得到1分。每一次,辽哥会选择一个碉堡进行攻击。所有未被破坏的碉堡会联合起来防御,因此为了破坏那个碉堡,辽哥的攻击力必须大于或者等于所有未被破坏的碉堡的防御值之和,否则辽哥就会输掉游戏。如果辽哥成功破坏了那个碉堡,那么他的攻击力会变成那个碉堡的附加值,然后他可以选择下一个攻击的目标。

由于辽哥拥有强大的编程能力,他不费吹灰之力就改写了那个游戏。在游戏开始前,他可以用炸弹消灭任意的碉堡,但是他不能通过这种方式来获得分数。问题来了,在游戏开始后,辽哥可以得到的最大分数是多少?

据闻是贪心加DP
炸掉碉堡就是不选择这些碉堡
设f[i]是剩下i个碉堡时所有碉堡最小的防御值
如果从前往后进行游戏很麻烦,所以
f[i]=min(f[i],f[i-1]+a[j]) {b[j]>=f[i-1]}
即这时还有i-1个碉堡,要选择上一个被炸的碉堡;这个被炸的碉堡,需要
防御值在可选的碉堡中最小
附加值大于已选的所有碉堡

排序就是按照a[i]+b[i]排序

#include <cstdio> 
#include <algorithm>
#include <cstring>

using namespace std;

int n,l,r,ans,s;
struct node{
	int f,g;
}a[1003];
int f[1003];

bool comp(node a,node b){
	return a.f+a.g>b.f+b.g;
}

int main(){
	while (~scanf("%d",&n)){
		memset(a,0,sizeof a);
		for (int i=1;i<=n;i++)
			scanf("%d%d",&a[i].f,&a[i].g);
		scanf("%d",&s);
		sort(a+1,a+1+n,comp);
		memset(f,0x3f,sizeof f);
		f[0]=0;
		for (int i=n;i;i--){
			for (int j=n;j;j--)
			if (a[i].g>f[j-1])
			f[j]=min(f[j],f[j-1]+a[i].f);
		}
		l=0,r=n;
		while (l<=r){
			int mid=(l+r)/2;
			if (f[mid]<=s) ans=mid,l=mid+1;
			          else r=mid-1;
		}
		printf("%d\n",ans);
	}
}

立阳二中、清华医学系谢俞

在这里插入图片描述

“我是谢俞。还有,我不涂黑指甲油”

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值