hihocoder 编程练习赛98 部分解题报告与题解

A - 推断上下级

HihoCoder - 1947
H公司包括CEO在内,一共有N名员工,编号1~N,其中CEO的编号是1。除了CEO之外,其他员工都有唯一一名直接上司,形成了一种树形的上下级关系。
现在小Hi知道H公司所有的上下级关系,一共M对。换句话说,只要两名员工A和B之间存在上下级关系(直接或者间接),那么A和B一定在这M对关系之中。
请你帮小Hi推断出每个人的直接上级是谁。

从题面可以看出,M对关系所构成的关系图不包含环,所以按入度拓扑排序可得到每个人的直接上级。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5+100;
vector <int> f[maxn];
int n,m;
int indegree[maxn];
int fa[maxn];
void topo(int n){
    int i,j,id,t=0;
    for(j=1;j<=n;j++){
        for(i=1;i<=n;i++){
            if(indegree[i]==0){
              id=i;
              break;    
            }
        }
        indegree[id]=-1;
        for(int i=0;i<f[id].size();i++){
            int k=f[id][i];
            fa[k] = id;
            indegree[k]--;
        }
    }
}
int x,y;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		f[x].push_back(y);
		indegree[y]++;
	}
	topo(n);
	for(int i=1;i<=n;i++)
	{
		printf("%d\n",fa[i]);
	}
	return 0;
}

B - H市规划
HihoCoder - 1949
H市的平面地图可以看成是一个NxM的方格矩阵,每一个格子代表H市的一个区域。
H市的市长给每一个方格都标记了一个0~2的数字,2代表该区域包含高校,1代表该区域包含创业园,0代表该区域既不包含高校也不包含创业园。(没有区域既包含高校也包含创业园)
现在H市长希望选出一片连续的区域(若干上下左右连在一起的方格)进行特别规划,要求
1)这片区域需要既包含高校也要包含创业园
2)这片区域面积尽量小,即包含方格的数目尽量少
请你帮H市长找出满足条件的一片区域,并且输出其包含方格的数目。

看起来像是类似平面上最近点对,可以考虑分治做法,把整个平面分成两块,最后在统计黑白两点分别在两块的情况。 (类比平面上最近的点对)

不过这道题数据有点弱,只要加几个特判的暴力就能卡过(甚至不加特判的暴力)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 1e6+100;
int f[1010][1010];
char s[1010][1010];
int cnt1,cnt2;
struct node
{
	int x,y,id;
};
node f1[maxn],f2[maxn];
int jump[1010],n,m;
bool cmp(node a,node b)
{
	if(a.x==b.x) return a.y<b.y;
	return a.x<b.x;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s[i]+1);
	}
	int ans= 1e9;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(s[i][j]=='1') f1[++cnt1].x=i,f1[cnt1].y=j,jump[i]++;
			if(s[i][j]=='2') f2[++cnt2].x=i,f2[cnt2].y=j;
			if(s[i][j]=='1'&&s[i-1][j]=='2') ans=2;
			if(s[i][j]=='1'&&s[i][j-1]=='2') ans=2;
			if(s[i][j]=='1'&&s[i+1][j]=='2') ans=2;
			if(s[i][j]=='1'&&s[i][j+1]=='2') ans=2;
		}
	}
	if(ans==2){
		cout<<2<<endl;
		return 0;
	}
	for(int i=1;i<=cnt1;i++)
	{
		for(int j=1;j<=cnt2;j++)
		{
			ans=min(ans,abs(f1[i].x-f2[j].x)+abs(f1[i].y-f2[j].y)+1);
		}
	}
	cout<<ans<<endl;
	return 0;
}

C - 二叉树

HihoCoder - 1951

给出一棵n个点的二叉树,所有节点从1到n编号,根节点的编号为1。
你需要给每个点标记0或者1。标记结束后,按照二叉树的先序遍历把标记写下,可以得到一个二进制数a(可能有前导0)。同样后序遍历也可以得到一个二进制数b。
求所有标号中,a-b的最大值。

求出先序遍历和后序遍历,我们考虑如何使先序遍历所代表的二进制数比后续大。 对于每一个节点如果其在先序中比在后序中靠前 - 此时让其等于1先序和后序的差值会变大 , 同理如果其在后序中的位置考前,再让其等于1会使后序变大,所以这种情况我们让其等于零。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#define LL long long
using namespace std;
const int maxn = 1e5+100;
const LL mod = 998244353;
int n;
struct node
{
	int ls,rs;
};
node T[maxn];
int h1[maxn],h2[maxn];
int a1[maxn],a2[maxn];
int cnt1,cnt2;
map <int,int > mp;
void dfs_frt(int u)
{
	h1[++cnt1] = u;
	if(T[u].ls) dfs_frt(T[u].ls);
	if(T[u].rs) dfs_frt(T[u].rs);
}	
void dfs_bck(int u)
{
	if(T[u].ls) dfs_bck(T[u].ls);
	if(T[u].rs) dfs_bck(T[u].rs);
	h2[++cnt2] = u;
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>T[i].ls>>T[i].rs;
	}
	dfs_frt(1);
	dfs_bck(1);
	//for(int i=1;i<=n;i++) cout<<h1[i]<<" "<<h2[i]<<endl;
	for(int i=1;i<=n;i++)
	{
		mp[h2[i]] = i;
	}
	for(int i=1;i<=n;i++)
	{
		int u = mp[h1[i]];
		if(u<=i) a1[i] = 0 , a2[u] = 0;
		else a1[i] = 1, a2[u] =1; 
	}
	LL r1=0,r2=0;
	for(int i=1;i<=n;i++)
	{
		if(a1[i]) r1=r1*2+1;
		else r1*=2;
		r1%=mod;
		if(a2[i]) r2=r2*2+1;
		else r2*=2;
		r2%=mod;
	}
	r1-=r2;
	while(r1<0) r1+=mod;
	cout<<r1<<endl;
	return 0;
}

F - 数组划分游戏

HihoCoder - 1948
小Hi在玩一个有关数组划分的游戏。给定一个整数K和一个长度为N的数组A=[A1, A2, … AN],小Hi需要将它划分为K个连续子数组,并对每个子数组求和。
不妨设这K个子数组的和依次是S1, S2, … SK,则小Hi的得分是其中的最小值即min(S1, S2, … SK)。
例如对于A=[1, 2, 3, 4]和K=2,小Hi可以划分成[1, 2]和[3, 4],这样得分是3;也可以划分成[1, 2, 3]和[4],这样得分是4。
对于给定的K和数组A,你能帮助小Hi算出他最多能得多少分吗?

F题竟然是最简单的题,从题面不难看出最小值最大很明显是二分,答案具有单调性,因为最小值越大划分次数越少。(数组元素都大于0)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#define LL long long
using namespace std;
const int maxn = 1e5+100;
const LL mod = 998244353;
int n,k;
LL a[maxn] ,l,r,ans;
bool check(LL val)
{
	LL res=0,cnt=0;
	for(int i=1;i<=n;i++)
	{
		res+=a[i];
		if(res>=val)
		{
			res=0;
			cnt++;
			if(cnt==k) return true;
		}
	}
	return false;
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		r+=a[i];
	}
	while(l<=r)
	{
		LL mid=(l+r)>>1;
		if(check(mid))
		{
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	cout<<ans<<endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值