SCOI2015 day1

SC省选day1(scoi2015)

       T1:大意:给出一个n*m的矩阵,从中选出n个数(m>=n),保证每一行每一列都只有一个数被选中,使得选出的数中第k大的最小。(n,m<=250)

       我们先把问题转化成求第n-k+1小的数最小,那么我们先二分答案,然后对于第i行,第j列的数,如果它小于当前val,从i到j连一条边,否则不管,如果最后的最大流大于等于n-k+1,就说明当前方案可行,否则不行。

 

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
struct node{int to;int next;int len;
};node bian[1000010];
int size = 1,first[1010],s,t,n,m,A[260][260],dis[1010],head,tail;
int p[100010],k,maxx = -1;
bool exist[1010];
void inser(int x,int y,int z) {
	bian[ ++ size].to = y;
	bian[size].next = first[x];
	first[x] = size;
	bian[size].len = z;
}
void build_edge(int x) {
	size = 1;
	memset(bian,0,sizeof(bian));
	memset(first,0,sizeof(first));
	s = 0,t = n + m + 1;
	for(int i = 1;i <= n;i ++)
	{
		inser(s,i,1);
		inser(i,s,0);
	}
	for(int i = 1;i <= m;i ++)
	{
		inser(n + i,t,1);
		inser(t,n + i,0);
	}
	for(int i = 1;i <= n;i ++)
		for(int j = 1;j <= m;j ++)
			if(A[i][j] <= x)
			{
				inser(i,n + j,1);
				inser(n + j,i,0);
			}
}
bool bfs(int x,int y) {
	memset(dis,127,sizeof(dis));
	memset(exist,false,sizeof(exist));
	dis[x] = 0;
	head = 0,tail = 1;
	p[1] = x;
	while(head != tail) 
	{
		int k = p[ ++ head];
		exist[k] = false;
		for(int u = first[k];u;u = bian[u].next)
			if(dis[bian[u].to] > dis[k] + 1 && bian[u].len > 0)
			{
				dis[bian[u].to] = dis[k] + 1;
				if(!exist[bian[u].to])
				{
					p[ ++ tail] = bian[u].to;
					exist[bian[u].to] = true;
				}
			}
	}
	if(dis[y] <= 1000000) return true;
	return false;
}
int dfs(int x,int flow) {
	if(x == t) return flow;
	int ret = 0;
	for(int u = first[x];u && flow > 0;u = bian[u].next)
		if(dis[bian[u].to] == dis[x] + 1 && bian[u].len > 0)
		{
			int D = dfs(bian[u].to,min(flow,bian[u].len));
			ret += D;
			flow -= D;
			bian[u].len -= D;
			bian[u ^ 1].len += D;
		}
	if(ret == 0 || flow == 0) dis[x] = -5;
	return ret;
}
int maxflow() {
	int ret = 0;
	while(bfs(s,t))
		ret += dfs(s,1000000000);
	return ret;
}
bool check(int x) {
	build_edge(x);
	int Match = maxflow();
	if(Match >= n - k + 1) return true;
	return false;
}
int main() {
	scanf("%d%d%d",&n,&m,&k);
	for(int i = 1;i <= n;i ++)
	{
		for(int j = 1;j <= m;j ++)
		{
			scanf("%d",&A[i][j]);
			maxx = max(maxx,A[i][j]);
		}
	}
	int l = 1,r = maxx;
	while(l != r)
	{
		int Mid = (l + r) >> 1;
		if(check(Mid)) r = Mid;
		else l = Mid + 1;
	}
	printf("%d\n",l);
	return 0;
}

       T2:大意:给出一个环与n个区间,要求求出对于所有的区间i,在选定了i区间之后最少再选择几个区间使得整个环被覆盖。

       我们先把区间排序后倍长(如果当前区间r<l(因为是环),我们就把r加上区间的长度来减少特判的情况)。

       我们这样考虑,选择了i,那么下一个选择的一定是能够和i相接,并且r尽量靠右的一个区间,这个我们可以用单调队列实现(据说可以二分…)。

       然后就可以倍增啦,设f[i][j]为选了i区间之后一共选了2^j个区间,下一个该选的区间是什么,A[i][j]设为选了之后的右区间最远是哪儿,对于每一个区间求出答案就可以了。

 

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
struct node{int l;int r;int Ans;int id;
};node line[600010];
int n,m,f[600010][21],A[600010][21],next,head,tail,g[600010];
bool comp(const node &x,const node &y) {
	if(x.l != y.l) return x.l < y.l;
	return x.r < y.r;
}
bool back(const node &x,const node &y) {return x.id < y.id;}
int main() {
	scanf("%d%d",&n,&m);
	if(n == 1) printf("1"),exit(0);
	for(int i = 1;i <= n;i ++) 
	{
		line[i].id = i;
		scanf("%d",&line[i].l);
		scanf("%d",&line[i].r);
		if(line[i].r <= line[i].l)
			line[i].r += m;	
	}
	sort(line + 1,line + n + 1,comp);
	for(int i = 1;i <= n;i ++)
	{
		line[n + i].id = i + n;
		line[n + i].l = line[i].l + m;
		line[n + i].r = line[i].r + m;
	}
	next = 2;head = 1,tail = 0;
	for(int i = 1;i <= 2 * n;i ++)
	{
		while(head <= tail && g[head] <= i) head ++;
		while(next <= 2 * n && line[next].l <= line[i].r)
		{
			while(head <= tail && line[g[tail]].r <= line[next].r)
				tail --;
			g[ ++ tail] = next;next ++;
		} 
		if(head <= tail) f[i][0] = g[head],A[i][0] = line[i].r;
	}	
	for(int i = 1;i <= 20;i ++)
	{
		for(int j = 1;j <= 2 * n;j ++)
		{
			A[j][i] = A[f[j][i - 1]][i - 1];
			f[j][i] = f[f[j][i - 1]][i - 1];
		}
	}
	for(int i = 1;i <= n;i ++)
	{
		int Now = i,ret = 0;
		for(int j = 20;j >= 0;j --)
			if(f[Now][j] != 0 && A[Now][j] - line[i].l < m)
				Now = f[Now][j],ret += (1 << j);
		line[i].Ans = ret + 1;
	}
	sort(line + 1,line + n + 1,back);
	for(int i = 1;i <= n - 1;i ++) printf("%d ",line[i].Ans);
	printf("%d",line[n].Ans);
	return 0;
}

       T3:大意:给出一个凸多边形,设点集s为满足使得点0,1,p(p为当前点集中的点)是所有的i,i+1,p中最小的三角形的点的集合,求点集s的面积占原多边形的面积比。

       半平面交。

       首先的限制条件是多边形的边,其次就是面积的限制,这个我们可以用叉积计算。

       其实挺简单的,但是太冗杂了,不写了。(其实是不会写….)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
中描述了一个幼儿园里分配糖果的问题,每个小朋友都有自己的要求。问题的输入包括两个整数NN和KK,表示幼儿园里的小朋友数量和要满足的要求数量。接下来的KK行表示小朋友们的要求,每行有三个数字,XX,AA,BB。如果X=1,表示第AA个小朋友分到的糖果必须和第BB个小朋友分到的糖果一样多;如果X=2,表示第AA个小朋友分到的糖果必须少于第BB个小朋友分到的糖果;如果X=3,表示第AA个小朋友分到的糖果必须不少于第BB个小朋友分到的糖果;如果X=4,表示第AA个小朋友分到的糖果必须多于第BB个小朋友分到的糖果;如果X=5,表示第AA个小朋友分到的糖果必须不多于第BB个小朋友分到的糖果。这个问题可以被看作是一个差分约束系统的问题。 具体地说,可以使用差分约束系统来解决这个问题。差分约束系统是一种通过给变量之间的关系添加约束来求解最优解的方法。对于这个问题,我们需要根据小朋友们的要求建立约束条件,并通过解决这个约束系统来得出最小的糖果数量。 在问题的输入中,X的取值范围为1到5,分别对应不同的关系约束。根据这些约束,我们可以构建一个差分约束图。图中的节点表示小朋友,边表示糖果数量的关系。根据不同的X值,我们可以添加相应的边和权重。然后,我们可以使用SPFA算法(Shortest Path Faster Algorithm)来求解这个差分约束系统,找到满足所有约束的最小糖果数量。 需要注意的是,在读取输入时需要判断X和Y是否合法,即是否满足X≠Y。如果X=Y,则直接输出-1,因为这种情况下无法满足约束条件。 综上所述,为了满足每个小朋友的要求,并且满足所有的约束条件,我们可以使用差分约束系统和SPFA算法来求解这个问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【差分约束系统】【SCOI2011】糖果 candy](https://blog.csdn.net/jiangzh7/article/details/8872699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [P3275 [SCOI2011]糖果(差分约束板子)](https://blog.csdn.net/qq_40619297/article/details/88678605)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值