UPC2022暑期个人训练赛第32场

问题 A: 窨井盖

题目描述

Jzt小时候走路的时候,有一个习惯,踩着窨井盖走。(不知道有没有人小时候也有这种做法……)
踩窨井盖很爽,但是,jzt不希望走得太慢,因此,他希望每一步走过的距离的最小值最大。为了快一点走,jzt只好忍痛割爱,跳过一些窨井盖。但是,如果跳过太多,又会觉得不爽,因此,他决定,最多可以跳过M个窨井盖。
你可以认为jzt的腿无限长,不用担心一步跨不到……

输入

第一行三个数L,N,M,分别表示家的位置,有N个窨井盖,可以跳过M个窨井盖。
接下来N行,每行一个数Di,表示N个窨井盖的位置

输出

输出一行一个数,表示最小距离的最大值。

样例输入

25 5 2
2
14
11
21
17

样例输出

4

提示

样例解释:跳过位置2和位置14的窨井盖,剩下相邻的窨井盖中距离最小的是4,是17到21或者21到25

1<=L<=1,000,000,000
0<=N<=50,000
0<=M<=N
0<Di<L
50%的数据,N<=100

一开始想作差然后排个序,选第m+1个,后来发现跳过井盖后,某些差值会变大,所以这个思路不行。不行,咱就枚举试试呗,但是鉴于1e9的数据量,用二分可以解决

代码

#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
int l,m,n;
int d[50005];
signed main()
{
    cin>>l>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>d[i];
    d[n+1]=l;
    sort(d,d+1+n);
    int ll=1,r=l+1,mid;
    while(ll<r)
    {
        mid=ll+r+1>>1;//因为mid是减一的避免死循环
        int k=0,q=0;//k是最小跳过距离为m时一定跳过的井盖的数量
        for(int i=1;i<=n+1;i++)
        {
            if(d[i]-q<mid)k++;//小于的才跳
            else q=d[i];
        }
        if(k>m)r=mid-1;//k>m说明这个m不可行
        else ll=mid;
    }
    cout<<ll;
    return 0;
}

问题 B: 约会序列

题目描述

众所周知,HYF有很多小姊妹。
HYF的小姊妹军团共有N个人,他每天选择一个不同的MM约会。现在HYF想把未来的N天里要约会的MM做一个计划……
首先,他按照自己的标准把N个MM分成A等~Z等,当然A等是质量最好的,Z等是质量最差的(可怜的Z等MM……),然后把她们随机地排成一个队列,比如ACDBCB。HYF决定每次选择队列最前或最后的MM约会,约过的MM就从队列中删去,这样就得到一个长度为N的约会序列。不同的选择方式会得到不同的约会序列,贪心的HYF当然希望先约质量高的MM啦!所以他希望得到所有约会序列中字典序最小的那个。
请你写一个程序帮他确定这样的约会序列。

输入

第一行一个正整数N,表示一共N个MM
接下来N行,每行一个大写字母,表示队列中第i个MM的级别

输出

一行一个长度为N的字符串,表示约会序列中字典序最小的那个。

样例输入

6
A
C
D
B
C
B

样例输出

ABCBCD

提示

30%的数据,N<=20
60%的数据,N<=100
100%的数据,N<=2000
这题。一开始死活55分把我挡住了,重写了一个没想到意外过了
就是在处理相同字母时需要考虑下,之前的策略是输出一个,然后到不行(两指针相等或者字母不等时判断下),估计有bug的,后来干脆输出交给下一波循环吧,数据较小。

代码

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int n;
char s[20010];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)cin>>s[i];
	for(int i=1,j=n;i<=j;)
	{
		if(s[i]<s[j])
		{
			cout<<s[i];
			i++;
		}
		else if(s[i]>s[j])
		{
			cout<<s[j];
			j--;
		}
		else
		{
			int l=i,r=j;
			while(l<=r)
			{
				if(s[l]!=s[r])break;
				l++,r--;
			}
			if(s[l]<s[r])
			{
				cout<<s[i];
				i++;
			}
			else 
			{
				cout<<s[j];
				j--;
			}
		}
	}
	return 0;
}

问题 C: 直角三角形

题目描述

在平面直角坐标系上有N个点。
编写程序,统计出这N个点能构成多少个两直角边分别平行于坐标轴的直角三角形。

输入

输入共有两行:
第1行:输入一个整数N,(3≤N≤500,000);
第2行到N+1行:每行两个正整数X,Y(1≤X,Y≤500,000),代表点的坐标。

输出

输出只有一行,
输出直角三角形的个数。

样例输入

【样例1】
3
4 2
2 1
1 2
【样例2】
6
10 10
20 10
10 20
20 20
30 20
30 30

样例输出

【样例1】
0
【样例2】
8

提示

对于全部40%的数据,保证N≤100;
对于全部70%的数据,保证N≤10,000;
对于全部的数据,保证N≤500,000;

这个题需要坐标系画图,可以用GeoGebra
样例二
image
有些眼花缭乱,我们分析一下,既然是直角三角形,而且直角边平行于坐标轴,那么对于一个点(x,y)它能和它同行和同列的点组成(l-1)*(r-1)个直角三角形,对于每个点枚举相加下就OK了,此题ans开long long
代码

#include<iostream>
#include<map>
#define int long long
using namespace std;
int n,l[500001],r[500001],ans;
struct point
{
	int x;int y;
}a[500001];
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
    	scanf("%lld %lld",&a[i].x,&a[i].y);
    	l[a[i].x]++;
    	r[a[i].y]++;
	}
	for(int i=1;i<=n;i++)
	{
		int x=a[i].x,y=a[i].y;
		if((l[x]>1)&&(r[y]>1))
			ans=ans+(l[x]-1)*(r[y]-1);
	}
	cout<<ans;
    return 0;
}

问题 D: 最勇敢的机器人

题目描述

机器人们都想知道谁是最勇敢的,于是它们比赛搬运一些物品。
它们到了一个仓库,里面有n个物品,每个物品都有一个价值Pi和重量Wi,但是有些物品放在一起会爆炸,并且爆炸具有传递性。(a和b会爆炸、b和c会爆炸则a和c会爆炸)机器人们可不想因此损失自己好不容易从Wind那里敲诈来的装备,于是它们想知道在能力范围内,它们最多可以拿多少价值的物品。
你能帮助它们吗?

输入

每组测试数据
第1行为n,Wmax,k(0<=n,Wmax,k<=1000)
接下来n行,为每个物品的Pi,Wi(0<=Pi<=1000,1<=Wi<=10,均为整数)
再接下来k行,每行2个数字a,b表示a和b会发生爆炸

输出

对每组数据输出1行,为最大可能价值

样例输入

3 10 1
100 1
200 5
10 5
1 2

样例输出

210

并查集+背包,最后73分有点小遗憾,我的思路是搞一个结构体,每加入一个物品就把它的f存进去,再加入的话判断一下,CSDN上看到一个不错的思路,同一结构体的存一组,每组只能选一个
我该复习DP和背包了

代码

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
#pragma GCC optimize(2)
using namespace std;
int n,wm,k;
int f[1001],t[1001][1001];
int an[1001];
int w[1001],v[1001];
inline int fin(int x)
{
    if(f[x]==x)return x;
    return f[x]=fin(f[x]);
}
inline void un(int x,int y)
{
    int xx=fin(x),yy=fin(y);
    if(xx!=yy)
    f[xx]=yy;
}

signed main()
{
    cin>>n>>wm>>k;
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
        cin>>v[i]>>w[i];
    }
    for(int i=1;i<=k;i++)
    {
        int x,y;
        cin>>x>>y;
        un(x,y);
    }
    for(int i=1;i<=n;i++)
    {
    	int p=fin(i);
    	t[p][++t[p][0]]=i;
	}
    for(int i=1;i<=n;i++)
    {
    	if(t[i][0]==0)continue;
        for(int j=wm;j>=0;j--)//这里不知道会用到哪一个物品,所以为0
        {
        	for(int l=1;l<=t[i][0];l++)
        	{
        		if(j>=w[t[i][l]])
        		an[j]=max(an[j],an[j-w[t[i][l]]]+v[t[i][l]]);
			}
		}
    }
    cout<<an[wm];
    return 0;
}

笔记本电池没电了,剩下明天继续补

posted @ 2022-08-12 00:38  qbning  阅读(32)  评论(0编辑  收藏  举报
描述