2020百度之星初赛二

1001(Poker)

Problem Description
小沃沃在玩一个有趣的游戏。
初始他有 n 块钱,每一轮他需要投入至少 m 块钱,系统会拿走其中 p% 的钱,并把剩下的钱还给他。
请问在最优情况下,小沃沃最多可以玩多少轮?
假设当前一轮小沃沃投入了 x 块钱,那么他可以收回 ⌊x×(1−p%)⌋ 块钱,其中 ⌊a⌋ 表示 a 取下整。
小沃沃每一轮投入的钱不能超过他现在拥有的钱。
每一轮投入的钱必须为整数。
题目提交:6775
类似题目:洛谷P1150
Input
第一行一个正整数 test(1≤test≤100000) 表示数据组数。
对于每组数据,一行三个整数 n,m,p(1≤n≤100000,1≤m≤1000,1≤p≤100)。

Output
对每组数据输出一行一个整数表示答案。

Sample Input
2
10 2 50
10 2 100

Sample Output
9
5

思路分析
想要情况最优,利用贪心思想,可以很轻易的得出每次投入最低要求 m 块钱,即可获得最大轮次,因此分量确定,根据公式,即可得出答案,然后AC。
tips:系统返还的钱是在下一轮才返还。

代码一:
公式原始递推(完整代码):
当作模拟题,根据题目给出的公式,算出玩一轮钱的变化(m确定,故答案为定值),然后用while循环,累加答案即可。
times = 254 ms

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll read() {
    ll x = 0, f = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
    return x * f;
}
void write(ll x) {
    if (x < 0) { putchar('-'); x = -x; }
    if (x >= 10) write(x / 10);
    putchar(x % 10 + '0');
}
int main()
{
    int t;
    t = read();
    while (t--)
    {
        int n, m, p, ans = 0;
        n = read(), m = read(), p = read();
        while (n >= m)
            n = (n - m) + (m * (100 - p) / 100), ans++;//公式计算,并累加答案
        write(ans);
        printf("\n");
    }
}

代码二:
减少冗余计算——>>>>过程优化(完整代码):
因为m是定值,所以我们可以一次性得出 n 块钱可以游玩几次(ans累加答案),然后将 余数 和 游玩次数*系统返还的值 累加回 n 。进行下一次计算,直到手上的钱不足以玩一轮后,退出。
times = 144 ms

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll read() {
        ll x = 0, f = 1; char ch = getchar();
        for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
        for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
        return x * f;
    }
void write(ll x) {
        if(x < 0) {putchar('-'); x = -x;}
        if(x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
int main()
{
    int t;
    t = read();
    while(t--)
    {
        int n,m,p,ans=0;
        n=read(),m=read(),p=read();
        while(n>=m)
        {
            int tmp = n%m;//游玩过后的余数
            n = n/m;//游玩的次数
            ans+=n;//答案数
            n = n*((m*(100-p))/100) + tmp;//下一轮手上持有的金钱数
        }
        write(ans);
        printf("\n");
    }
}

1002(Distance)

Problem Description
小沃沃所在的世界是一个二维平面。他有 n 个朋友,第 i 个朋友距离他的距离为 a[i],小沃沃并不知道这些朋友具体在什么点上。
请问在最优情况下,小沃沃的朋友两两之间的欧几里得距离的和的最小值是几?
假设小沃沃的位置为 P0=(x0,y0),第 i 个朋友的位置为 Pi=(xi,yi),对于所有的 i,需要满足 dist(P0,Pi)=a[i],并且∑n−1i=1∑nj=i+1dist(Pi,Pj) 最小,其中 dist(X,Y) 为连接点 X 和点 Y 的线段的长度。xi,yi 都可以是任意实数。
题目提交:6776

Input:
第一行一个正整数 test (1≤test≤10) 表示数据组数。
对于每组数据,第一行一个正整数 n (1≤n≤100000)。
接下来一行 n 个整数,第 i 个整数 a [i] (1≤a[i]≤1000000000) 表示第 i 个朋友和小沃沃的距离。

Output
对每组数据输出一行一个数,表示 ∑n−1i=1∑nj=i+1dist(Pi,Pj) 的最小值。答案需要四舍五入到整数。

Sample Input
2
2
3 5
5
1 2 3 4 5

Sample Output:
2
20

思路分析:
两点之间直线最短,而想要所有点两两之间获得的(欧几里得)距离最短,即所有点在一条直线上 ------- 前置条件
解法分析:

  1. 枚举累加 愚蠢的地球人 O(n^2),华丽超时,星空般绚丽。

  2. 思维简化(times = 604 ms):

    例如数据
    5
    1 3 4 8 2
    在满足前置条件的基础上,获得两两最短距离,第一步先排序。
    1 2 3 4 8
    计算出两两之间的距离差值
    1 1 1 4 (为啥少一项就不用我说了吧)
    题目要求是计算每个点和其他所有点的距离,过程演示

    第一个点和其他所有点的距离和:
    1
    1 + 1
    1 + 1 + 4
    第二个点和其他所有点的距离和:
    ****1
    ****1 + 4
    第三个点和其他所有点的距离和:
    ********4

    观察发现:(原来有1 1 1 4 四个项)
    第一个差值计算了 ans1 = (n-1) = 3
    第二个差值计算了 ans2 = (n-2) + ans1 - (i - 1)(之前计算第一个差值时,第二个差值也有计算,且数量为之前的计算数量减 i - 1次,不懂看过程)= 4
    第三个差值计算了 ans3 = (n-3) + ans2 -(i - 1) = 3

    根据规律和递推式,可以计算出每一个差值使用的次数,最后遍历将 差值和使用次数的乘积 累加到答案中即可。

#include <bits/stdc++.h>
#define ll long long
const int maxn = 1e6+7; 
using namespace std;
ll date[maxn],st[maxn];
ll read() {
        ll x = 0, f = 1; char ch = getchar();
        for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
        for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
        return x * f;
    }
void write(ll x) {
        if(x < 0) {putchar('-'); x = -x;}
        if(x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
int main()
{
    int t;
    t = read();
    while(t--)
    {
        memset(date,0,sizeof(0));
        int n,cnt=1;
        long double ans = 0;
        n=read();
        for(int i=1;i<=n;++i)
            date[i] = read();
        sort(date+1,date+1+n);
        for(int i =2;i<=n;++i)
            st[cnt++] = date[i]-date[i-1];
            date[1] = n-1;
        for(int i=2;i<=n;++i)
            date[i] = (n-i) + date[i-1] - i +1;
           for(int i=1;i<cnt;++i)
               ans+= date[i]*st[i];
        write(ans);
        printf("\n");
    }
}


1003(Covid)

Problem Description:
科学家小沃沃在研究病毒传播的规律,从而控制疫情。
有 n 个人,编号分别为 1,2,…,n。我们用荧光粉代替病毒,编号为 1 的人,在第 0 时刻涂上了荧光粉,剩下的人在第 0 时刻没有涂。
对于第 i 个人,我们知道这个人在哪些时刻出现在了哪些地方。
如果时刻 t,某个人和身体上有荧光粉的人,出现在了同一地点,那么从时刻 t 以后,这个人也会沾上荧光粉。
从小到大输出实验结束后身体上有荧光粉的人的编号。
题目提交:6777

input:
第一行一个整数 T(1≤T≤20) 表示 T 组数据。
对于每组数据,第一行一个整数 n(1≤n≤20000) 表示 n 个人。
对于第 i 个人,第一行输入一个整数 len[i] (1≤len[i]≤100) 表示这个人的活动轨迹。
接下来 len[i] 行,每行输入两个整数 t,p(1≤t≤100,1≤p≤10) 表示这个人 t 时刻出现在了 p 位置,保证 t 按严格递增的顺序给出。
除了这 len[i] 个时刻,这个人都呆在家里,或者换句话说,他/她不在任何位置。
保证len[[1]]+len[[2]]+…+len[n]≤200000。

Output:
对于每组数据输出一行,表示所有患者的编号。按编号从小到大输出。

Sample Input:
2
4
2
1 1
2 2
3
2 2
3 3
4 4
1
4 4
1
2 1
3
3
1 1
3 1
6 1
3
4 1
5 1
6 1
1
5 1

Sample Output:
1 2 3
1 2

样例解释
Case 1:
第 2 时刻,位置 2,1 与 2 相遇,2 沾上了。
第 4 时刻,位置 4,2 与 3 相遇,3 沾上了。

思路来源(转载)----》》Link,模拟。

AC代码:times = 202 ms

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
#define LL long long
#define mod 1e9 + 7
#define mem(x,y) memset(x,y,sizeof(x))
const int maxn = 2e6 + 7;
using namespace std; 
int read()
{
    int x = 0, f = 1;char c = getchar();
    while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
    return f * x;
}
void write(int x)
{
    if (x < 0) { putchar('-'); x = -x; }
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}
vector<pair<int, int>> people[110];//记录在第t秒,有多少人行动了,即在第t秒编号为id的人的出现在地点local 
vector<int> tag[110][15];//记录在第t秒p地点都有哪几个人
int vis[maxn];//记录哪些人感染了
int main()
{
    int t;t = read();
    while (t--)
    {
        int n = read(), sum = 0;//sum记录感染人数
        mem(vis, 0), vis[1] = 1;//编号为1的已经被感染
        //初始化
        for (int i = 0; i < 110; ++i)
        {
            people[i].clear();
            for (int j = 0; j < 15; ++j)
                tag[i][j].clear();
        }
        //输入
        for (int i = 1; i <= n; ++i)
        {
            int num = read();
            for (int j = 0; j < num; ++j)
            {
                int tmp1 = read(), tmp2 = read();
                people[tmp1].push_back(make_pair(i, tmp2));
                tag[tmp1][tmp2].push_back(i);
            }
        }
        vector<pair<int, int>>::iterator it;
        vector<int>::iterator it1;
        for (int i = 1; i <= 100; ++i)
        {
            if (people[i].empty()) continue;//这一秒没有人出行,直接跳过,否则,进行下面的步骤
            for (it = people[i].begin(); it != people[i].end(); ++it)//有人出行
            {
                int id = (*it).first, local = (*it).second;
                if (vis[id]&& !tag[i][local].empty())//这个人已被感染
                    for (it1 = tag[i][local].begin(); it1 != tag[i][local].end(); ++it1)
                        if (!vis[*it1]) vis[*it1] = 1, sum++;//使没感染病毒的人感染上,感染人数+1
            }
            if (sum == n) break;//如果已经全部感染,就不用再向下遍历了
        }
        write(1);
        for (int i = 2; i <= n; ++i)
            if (vis[i]) printf(" "), write(i);
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cjg20010316/article/details/107589396
今日推荐