hihoCoder 编程练习赛第45场

一共四道题

题目1 : 互补二元组

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定N个整数二元组(X1, Y1), (X2, Y2), … (XN, YN)。

请你计算其中有多少对二元组(Xi, Yi)和(Xj, Yj)满足Xi + Xj = Yi + Yj且i < j。

输入
第一行包含一个整数N。

以下N行每行两个整数Xi和Yi。

对于70%的数据,1 ≤ N ≤ 1000

对于100%的数据,1 ≤ N ≤ 100000 -1000000 ≤ Xi, Yi ≤ 1000000

输出
一个整数表示答案。

样例输入
5
9 10
1 3
5 5
5 4
8 6
样例输出
2

最初看到这道题的时候没有认真审题,认为(1,2)(3,2)(3,2)这种情况只计数一次(应该记两次),想办法将配过对的数组删除,结果耽误了很长时间,后来先做的后面几题,快结束时返回来再直接按字面意思做,过了70%(LTE)。比赛结束后翻到了某大神简洁的代码,膜拜。这道题将给定的条件Xi + Xj = Yi + Yj稍微变换一下可得 (Xi -Yi)+ (Xj - Yj) = 0, 所以将输入的数对作差值存储起来即可。考虑到X、Y的取值范围,可用map直接查找。嗯,以上的都能想到。大神的方法妙在存储X-Y时,直接对Y-X做加操作,省去了重复遍历和查找,很巧妙。(注意X,Y的取值范围,LL)

代码:

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
typedef long long ll;

using namespace std;
map<ll, ll> res;

int main()
{
    int N; cin >> N;

    ll cnt = 0;
    int  x, y;

    while(N--) {
        cin >> x >> y;
        cnt += res[x-y];
        res[y-x]++;
    }


    cout << cnt << endl;
    return 0;
}

题目2 : 寻找切线

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定平面上N个点P1=(X1, Y1), P2=(X2, Y2), … PN=(XN, YN)。

请你从中找到两个不同的点Pi和Pj满足:其他所有点都在Pi和Pj连线的同一侧(可以在连线上)。

如果有多组答案满足条件,你可以输出任意一组。

输入
第一行包含一个整数N。

以下N行每行包含两个整数Xi和Yi。

对于50%的数据,1 ≤ N ≤ 1000

对于100%的数据,1 ≤ N ≤ 100000 0 ≤ Xi, Yi ≤ 1000000

输出
输出由一个空格隔开的两个整数i和j,注意1 ≤ i, j ≤ N且i ≠ j。

样例输入
6
0 10
7 0
8 8
10 18
15 13
20 4
样例输出
5 6

方法:一条直线由两点确定:先找到 x/y 坐标最大值(最小值也可以)作为第一个点,再计算其余点与该点的斜率,取绝对值最大的点作为第二个点。

#include<iostream>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
#include<cmath>
#include<sstream>

using namespace std;

int main()
{
    int n; cin >> n;

    int x[n], y[n];
    int m = 0, q = 0;
    for(int i=0; i<n; i++) {
        cin >> x[i] >> y[i];
        if(x[i]>x[m]) m = i;
    }
    float k = 0;
    for(int i=0; i<n; i++)
    {
        if(i==m) continue;
        if(x[i]==x[m]) {q = i; break;}
        float k1 = abs(1.0*(y[i]-y[m])/(x[i]-x[m]));
        if(k1 > k) { k = k1, q = i;}

    }

    if(m < q)   cout << m+1 << ' ' << q+1 << endl;
    else cout << q+1 << ' ' << m+1 << endl;

    return 0;
}

题目3 : LR问题

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定两个字符串S和T,每个字符串都由’L’、’R’和’_’组成。例如:

R__LR_R_L

每一次操作,可以将S中的一个’R’与它右边相邻的’‘交换(前提是这个R右边相邻位置就是’‘);或者将S中的一个’L’与它左边相邻的’‘交换(前提是这个L左边相邻位置就是’‘)。

例如,先移动第一个L,再移动第二个R会得到:

R__LR_R_L -> R_L_R_R_L -> R_L__RR_L

请你计算最少经过几次操作可以将S变成T。

输入
第一行包含一个字符串S。

第二行包含一个字符串T。

对于30%的数据,1 ≤ |S| = |T| ≤ 20

对于100%的数据,1 ≤ |S| = |T| ≤ 100000

输出
输出一个整数代表最少操作次数。

如果不论如何S也不能变成T,输出-1。

样例输入
R__LR_R_L
R_L__RR_L
样例输出
2

总是把简单问题复杂化,竟然想用dp,做的题量还是太少了。参照某大神简洁优美的代码如下:

#include <iostream>
#include <vector>
using namespace std;
int main(){
    string s, t; cin>>s>>t;
    vector<int> ss, tt;
    for(int i=0;i<int(s.size());i++) if(s[i] != '_') ss.push_back(i);
    for(int i=0;i<int(t.size());i++) if(t[i] != '_') tt.push_back(i);
    if(ss.size() != tt.size()) return cout<<-1<<endl, 0;
    int ans = 0;
    for(int i=0;i<(ss.size());i++){
        int sx = ss[i], tx = tt[i];
        if(s[sx] != t[tx]) return cout<<-1<<endl, 0;
        if(s[sx] == 'L' && sx < tx) return cout<<-1<<endl, 0;
        if(s[sx] == 'R' && sx > tx) return cout<<-1<<endl, 0;
        ans += abs(sx - tx);
    }
    cout<<ans<<endl;
    return 0;
}

题目4 : 推断大小关系
时间限制:20000ms
单点时限:2000ms
内存限制:256MB
描述
有N个整数A1, A2, … AN,现在我们知道M条关于这N个整数的信息。每条信息是:

Ai < Aj 或者 Ai = Aj

小Hi希望你能从第一条信息开始依次逐条处理这些信息。一旦能推断出A1和AN的大小关系就立即停止。

输出在处理第几条时第一次推断出A1和AN的关系。如果处理完全部M条信息还是不知道A1和AN的大小关系,输出-1。

保证M条信息是没有矛盾的。

输入
第一行包含两个整数N和M。

以下M行每行包含一条信息Ai < Aj 或者 Ai = Aj。

对于30%的数据,1 ≤ N ≤ 1000, 1 ≤ M ≤ 10000

对于100%的数据,1 ≤ N ≤ 100000, 1 ≤ N ≤ 1000000

输出
一个整数表示答案。

样例输入
5 8
A1 < A3
A3 < A2
A3 < A4
A5 < A2
A1 < A4
A1 < A2
A5 < A1
A5 < A3
样例输出
7

参考了某大神的答案,嗯,,看懂了。知识点:二分查找、dfs

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;

const int maxn = 1e6 + 10;
int x[maxn], op[maxn], y[maxn];
int n, m;

int fa[maxn];
int Find(int x)
{
    return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
void Union(int x, int y)
{
    x = Find(x), y = Find(y);
    fa[x] = y;
}

int vis[maxn];
vector<int> G[maxn];
void dfs(int x)
{
    for(int i = 0; i < G[x].size(); i++)
    {
        int to = G[x][i];
        if(vis[to]) continue;
        vis[to] = 1, dfs(to);
    }
}

bool check(int mid)
{
    for(int i = 1; i <= n; i++) fa[i] = i, G[i].clear();
    for(int i = 1; i <= mid; i++)
        if(op[i] == 0) Union(x[i], y[i]);
    for(int i = 1; i <= mid; i++)
    {
        if(op[i] == 1) G[Find(x[i])].push_back(Find(y[i]));
        if(op[i] == -1) G[Find(y[i])].push_back(Find(x[i]));
    }
    for(int i = 1; i <= n; i++) vis[i] = 0;
    vis[Find(1)] = 1;
    dfs(Find(1));
    if(vis[Find(n)]) return true;
    for(int i = 1; i <= n; i++) vis[i] = 0;
    vis[Find(n)] = 1;
    dfs(Find(n));
    if(vis[Find(1)]) return true;
    return false;
}

int id(char * s)
{
    int l = strlen(s);
    int ret = 0;
    for(int i = 0; i < l; i++) ret = ret * 10 + (s[i] - '0');
    return ret;
}

int main(void)
{
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; i++)
    {
        char a[11], b[11], c[11];
        scanf("%s %s %s", a, b, c);
        cout << a << ' ' << b  << ' '<< c << endl;
        x[i] = id(a + 1), y[i] = id(c +1);
        cout << x[i]  << ' ' << y[i] << endl;
        if(b[0] == '>') op[i] = 1;
        if(b[0] == '<') op[i] = -1;
    }
    if(!check(m)) puts("-1");
    else
    {
        int l = 1, r = m;
        while(l < r)
        {
            int mid = l + (r - l) / 2;
            if(check(mid)) r = mid;
            else l = mid +1;
        }
        printf("%d\n", r);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_24153697/article/details/79123465