队内排位赛(二)

比赛地址

A. Fence Planning

题目大意

在二维坐标轴上给你n个点,并且告诉你m条边,求出需要完全围住一个联通块的矩形周长的最小值

题目分析

显然本题需要用到并查集,而围住一个集合中所有点的矩形周长最小值为:(y_max-y_min)*2 + (x_max-x_min)*2

代码
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int p[N];
LL n,m,x[N],y[N];
bool st[N];
struct Node
{
	LL x,y;
}node[N];
vector<Node> q[N];

int find(int x)
{
    if(p[x]!=x)p[x] = find(p[x]);
    return p[x];
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)p[i] = i;
	for(int i = 1; i <= n; i ++)
	scanf("%lld%lld",&node[i].x,&node[i].y);
	for(int i = 1; i <= m; i ++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		p[find(a)] = p[find(b)];
	}
	for(int i = 1; i <= n; i ++)
	{
		int f = find(i);
		q[f].push_back(node[i]);
		st[f] = true; 
	}
	LL ans =-1;
	for(int i = 1; i <= n; i ++)
	if(p[i]==i)
	{
		LL x_min = q[i][0].x, x_max = q[i][0].x, y_min = q[i][0].y, y_max = q[i][0].y;
		for(int j = 0; j < q[i].size(); j ++)
		x_min = min(x_min,q[i][j].x),y_min = min(y_min,q[i][j].y),x_max = max(x_max,q[i][j].x),y_max = max(y_max,q[i][j].y);
		if(ans == -1 || ans > (x_max-x_min)+(y_max-y_min))ans = (x_max-x_min)+
		(y_max-y_min);
	}
	cout << ans*2;
	return 0;
}

B. Snakes

题目大意

有n组蛇,你需要用网来捕捉它们,网有一个大小属性s
ize,可以捕捉x条蛇(x<=size)同时会浪费size-x的空间,初始网的大小可以任意,中途可以换k次网,问最小浪费空间为多少。

题目分析

不难看出这是一道dp的问题,但个人认为本题的难度在于dp维度的选择,按照套路一般会有一个决定终点位置的维度,还有一个可以是最后一个网的起点位置,但是状态转移所需时间太长,且无法判断网的个数,若想判断网的个数,那么最好是以总共有多少种网作为第二维度。
而经过分析可以知道,在[l,r]这段区间内如果只能用一张网的话那么最小的浪费值为:m(r-l+1)-s[r]+s[l-1],其中m为该区间内的最大值,s数组为前缀和数组
所以会有三次遍历,首先遍历总共网的种数,然后遍历终点,最后遍历起点,如果想要在o(1)的时间内求出m,可以把起点从大到小遍历,然后在线更新即可,也可以先预处理出来这样的数组

代码
#include <iostream>
#include <cstring>
using namespace std;

const int N = 410;
int n,k,a[N],s[N],f[N][N];
int main()
{
	cin >> n >> k;
	for(int i = 1; i <= n; i ++)
	{
		cin>>a[i];
		s[i] = s[i-1]+a[i]; 
	}
	memset(f,0x3f,sizeof f);
	for(int i = 0; i <= k+1; i ++)f[i][i] = 0;//初始状态为前i个位置用i个网,空间无浪费 
	
	for(int i = 1; i <= k+1; i ++)//枚举总共有几个网 
	{
		for(int j = i+1; j <= n;j++)//枚举最后一个网终点位置 
		for(int t = j,tmp = 0; t>=i; t --)//枚举最后一个网起点位置 
		{
			tmp = max(tmp,a[t]);
			f[j][i] = min(f[j][i],f[t-1][i-1] + tmp*(j-t+1)-s[j]+s[t-1]);
		}
	}
	cout << f[n][k+1];
	return 0;
}

D. Left Out

题目大意

给你一个n×n并且只含LR的方阵,有以下两种操作:
1.把某一列L、R对调
2.把某一行L、R对调
问是否经历若干次操作后能否做到除了某一个点其余点都是L或R,若存在,输出这个点,某则输出-1

题目分析

首先,LR的方阵我们可以看成一个01矩阵

最暴力的做法:

枚举每一行每一列是否经行这个操作,那么操作的时间复杂度为O(4n)

如何优化:

如果我们想把第一行全部变为与(1,1)相同的元素,那么我们看第一行的2~n列是否与(1,1)元素相同,若不相同则对这一列经行操作,否则不操作,对于变为1或对于第一列也是同理,这样操作的复杂度就降为O(n)了

进一步分析:

但是我们发现程序连样例都过不了,因为如果错误的点恰好在第一行或者第一列的话,那么我们的操作就有问题了,但是我们将点的范围缩小了。
1.如果点为(1,1)
那么我们对列与对行的操作其实是相反的,也就是说你本应操作的但没操作,没有操作的却操作了,这样我们只需对2~n行与列再全部都进行一次操作即可,这样等同于对第一行与第一列经行操作
2.如果点不为(1,1)但在第一行或第一列上
这样我们对某一行或者某一列进行这样的操作就可以了

多组答案:

显然,如果有多组答案,那么n=2,且每个点都是答案

最后,我们只需按题目要求的排序方式来枚举答案即可

代码
#include <iostream>

using namespace std;
const int N = 1010;
char op[N];
bool st[N][N];
int n,x[N],y[N];
//修改行 
void modir(int x)
{
	for(int i = 1; i <= n; i ++)
	st[x][i] = !st[x][i];
}
//修改列 
void modic(int x)
{
	for(int i = 1; i <= n; i ++)
	st[i][x] = !st[i][x];
}

bool check()
{
	int cnt[2]={0,0},x0,y0,x1,y1;
	for(int i = 1; i <= n;i ++)
	for(int j = 1; j <= n; j ++)
	if(st[i][j]) cnt[1]++,x1=i,y1=j;
	else cnt[0]++,x0=i,y0=j;
	
	if(cnt[0]==1)
	{
		cout << x0<<' ' << y0;
		return true;
	}
	else if(cnt[1]==1)
	{
		cout << x1 << ' ' << y1 ;
		return true;
	}
	return false;
	
}
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> op+1;
		for(int j = 1; j <= n; j++)
		st[i][j] = (op[j]=='L');
	}

	for(int i = 2; i <= n; i ++)
	{
		if(st[1][i]!=st[1][1])modic(i);
		if(st[i][1]!=st[1][1])modir(i);
	}
	
	//检查(1,1) 
	modir(1),modic(1);
	if(check())return 0; 
	
	modir(1),modic(1);
	if(check())return 0;
	
	int cnt = 0;
	for(int i = 1; i<=n;i++)
	for(int j = 1; j<=n; j ++)
	if(st[i][j]!=st[1][1])
		cnt++,x[i]++,y[j]++;
	
	if(cnt==n-1)
	{
		for(int i = 2; i <= n; i++)
		if(x[i]==n-1)
		{
			cout << i << ' ' << 1;
			return 0;
		}
		else if(y[i]==n-1)
		{
			cout << 1 << ' ' <<i;
			return 0;
		}
	}
	cout << -1;
	return 0;
 } 

F. Milk Factory

题目大意

有n个点,n-1条有向边,问是否存在一个点能从其他任何点过来

分析

因为数据较小,直接暴搜就ac

代码
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int N = 110;
vector <int> point[N];
bool vis[N];
void dfs(int now, int to, bool &flag)
{
	if(now == to)flag = true;
	if(flag)return;
	vis[now] = true;
	for(int i = 0; i < point[now].size(); i ++)
	{
		int t = point[now][i];
		if(!vis[t])
			dfs(t,to,flag);
	}
}
int main()
{
	int n;cin >> n;
	for(int i = 1; i <= n-1; i ++)
	{
		int a,b;cin >> a >> b;
		point[a].push_back(b);
	}
	for(int i = 1; i <= n; i ++)
	{
		bool flag;
		for(int j = 1; j<=n;j++)
		{
			flag = false;
			memset(vis,0,sizeof vis);
			dfs(j,i,flag);
			if(!flag)break;
		}
		if(flag)
		{
			cout << i << endl;
			return 0;
		}
	}
	cout << -1;
	return 0;
}

G. Bucket Brigade

题目大意

给你一个起点和一个终点,还有一堆障碍物,求起点和终点的最短路

题目分析

BFS

代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct Point
{
	int x,y;
};
char m[20][20];
int f[20][20],dx[4] = {0,0,-1,1},dy[4]={1,-1,0,0};
vector<Point> barn, lake, rock;
int main()
{
	for(int i = 1; i <= 10; i ++)
	cin >> m[i]+1;
	queue<Point> q;
	for(int i = 1; i <= 10; i ++)
	for(int j = 1; j <= 10; j ++)
	{
		if(m[i][j] == 'B')	
		{
			q.push({i,j});
			f[i][j] = 1;
		}
		else if(m[i][j] == 'L')lake.push_back({i,j});
		else if(m[i][j] == 'R')f[i][j] = -1;
	}
	while(q.size())
	{
		int x = q.front().x, y = q.front().y;
		q.pop();
		for(int i = 0; i < 4; i ++)
		{
			int tx = dx[i]+x, ty = y +dy[i];
			if(tx>0&&tx<=10&&ty>0&&ty<=10&&m[tx][ty]!='R'&&!f[tx][ty])
			{
				q.push({tx,ty});
				f[tx][ty] = f[x][y]+1;
			}
		}
	}

	int x = lake[0].x,y=lake[0].y;
	cout << f[x][y] -2;
	return 0;
}

H. I Would Walk 500 Miles

题目大意

有N头奶牛,编号为1~N。x,y(x<y)两头奶牛相见的距离为(2019201913x+2019201949y) mod 2019201997。现将N头奶牛分为k组,求两头在不同组的奶牛的距离的最小值的最大值。

题目分析

首先初读题,若把奶牛看为点很容易联想到图。分成k组相当与建立n-k条边,那么只需把所有边排序然后再添加适当的n-k条边就好了,但这种方法时间复杂度为n方logn,想要对此优化,可以处理出任意奶牛x与编号后面所有奶牛的距离的最小值,即设f(x) = min(g[x][x+1],…g[x][n]),则将f(x)排序后第n-k+1即为答案。

代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL N = 7510,mod = 2019201997;
LL ans[N];
int main()
{
	LL n,k;
	cin >> n >> k;
	for(int i = 1; i <= n; i ++)
	ans[i] = mod;
	for(LL i = 1; i <= n; i ++)
	for(LL j = i+1; j <= n; j ++)
		ans[i] = min(ans[i],((LL)2019201913*i+(LL)2019201949*j)%mod );
	sort(ans+1,ans+1+n);
	cout <<ans[n-k+1]<<endl;
 	return 0;
 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值