0
点赞
收藏
分享

微信扫一扫

贪心算法--DP--国王的游戏--codeforces

佃成成成成 2022-03-14 阅读 60

国王的游戏
 

恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

输入描述:

第一行包含一个整数 n ,表示大臣的人数。
第二行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示国王左手和右手上的整数。
接下来 n 行,每行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

输出描述:

一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

示例1

输入

3 1 1 2 3 7 4 4 6

3 
1 1 
2 3 
7 4 
4 6

输出

2

2

说明

按 1 、 2 、 3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 ;
按 1 、 3 、 2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 ;
按 2 、 1 、 3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 ;
按 2 、 3 、 1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9 ;
按 3 、 1 、 2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 ;
按 3 、 2 、 1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9 。
因此,奖赏最多的大臣最少获得 2 个金币,答案输出 2 。

备注:

对于 20%的数据,有 1≤ n≤ 10,0 < a,b < 8 ;
对于 40%的数据,有 1≤ n≤20,0 <a,b<8 ;

对于 60%的数据,有 1≤ n≤100 ;

对于 60%的数据,保证答案不超过 109 ;

对于 100%的数据,有 1 ≤ n ≤1,000,0 < a,b < 10000 。

#include<iostream>
#include<cstdio>
#include <vector>
#include<algorithm>
using namespace std;
struct node
{
    int x;
    int y;
}m[1001];
bool compare(const node& h1, const node& h2)
{
    return h1.x * h1.y < h2.x* h2.y;
}//自定义比较函数
int cheng[500000], tail;//储存乘积,tail为长度,未压位
int ans[500000], taila;//储存答案
int pass[500000], tailp;//储存商
void div(int x)
{
    int i = tail;
    int now = 0;
    while (i > 0)//高精除低精除法
    {
        now = now * 10 + cheng[i];
        pass[i] = now / m[x].y;
        now = now % m[x].y;
        i--;
    }
    tailp = tail;
    while (pass[tailp] == 0 && tailp)    //更改长度
        tailp--;
}
void max()
{
    bool exchange = false;//是否交换的flag
    if (taila < tailp)
        exchange = true;
    if (taila == tailp)//相同长度比较
        for (int i = tailp; i >= 1; i--)
        {
            if (pass[i] > ans[i])
            {
                exchange = true;
                break;
            }
            if (pass[i] < ans[i])
                break;
        }
    if (exchange)
    {
        for (int i = tailp; i >= 1; i--)
            ans[i] = pass[i];
        taila = tailp;
    }
    return;
}
void mul(int x)
{
    int p = 0;
    for (int i = 1; i <= tail; i++)
    {
        cheng[i] = cheng[i] * m[x].x + p;
        p = cheng[i] / 10;
        cheng[i] %= 10;
    }
    while (p)//p有可能不是一位数
    {
        cheng[++tail] = p % 10;
        p /= 10;
    }
}
int main()
{
    //cin.sync_with_stdio(false);
    int n;
    cin >> n;
    int a, b;
    vector<int> v(4, 0);
    vector<int> mm = { 1,2,3,4 };
    v = mm;

    scanf("%d %d", &a, &b);

    for (int i = 1; i <= n; i++)
        scanf("%d %d", &m[i].x, &m[i].y);
    sort(m + 1, m + 1 + n, compare);
    while (a)//将国王的左手转换成高精度储存
    {
        cheng[++tail] = a % 10;
        a /= 10;
    }
    for (int i = 1; i <= n; i++)
    {
        div(i);//高精除低精
        max();//比较,因为有可能最后一个不是最大的
        mul(i);//高精度乘法,当然是低精
    }
    for (int i = taila; i >= 1; i--)
        printf("%d", ans[i]);//输出
    return 0;
}

 codeforces
 

题目描述

牛牛正在打一场CF
比赛时间为T分钟,有N道题,可以在比赛时间内的任意时间提交代码
第i道题的分数为maxPoints[i],题目的分数随着比赛的进行,每分钟减少pointsPerMinute[i]
这是一场比较dark的Cf,分数可能减成负数
已知第i道题需要花费 requiredTime[i] 的时间解决
请问最多可以得到多少分
 

输入描述:

第一行输入两个整数N,T (1 ≤ N ≤ 50, 1 ≤ T ≤ 100000)
第二行输入n个整数maxPoints[i]
第三行输入n个整数pointsPerMinute[i]
第四行输入n个整数requiredTime[i]
1 ≤ maxPoints[i],pointsPerMinute[i],requiredTime[i] ≤ 100000

输出描述:

输出一个整数

示例1

输入

1 74 502 2 47

1 74
502
2
47

输出

408

408

示例2

输入

2 40000 100000 100000 1 100000 50000 30000

2 40000
100000 100000
1 100000
50000 30000

输出

0

0

示例3

输入

3 75 250 500 1000 2 4 8 25 25 25

3 75
250 500 1000
2 4 8
25 25 25

输出

1200

1200

示例4

输入

3 30 100 100 100000 1 1 100 15 15 30

3 30
100 100 100000
1 1 100
15 15 30

输出

97000

97000

备注:

子任务1: n <= 10
子任务2: n <= 20
子任务3: 无限制

由于本题的做题选择会影响最后的得分,所以需要知道每一道题的优先级(参考牛客题解)

下面我们来推导一下,如何来选择做题顺序,即每一道题的优先级

对于两道题t1,t2来说,有两种做题顺序,如下图的C12,C21,我们定义P1为t1的每分钟减小的分数,T1为做题需要的时间

两道题的初始总分数为sum(最大分数之和)

得分C12=sum-T1*P1-(T1+T2)*P2;

得分C12=sum-T2*P2-(T1+T2)*P1;

则 C12-C21=T2*P1-T1*P2

要判断两道题的大小,这上式同时除T1*T2的P1/T1-P2/T2 这样我们就发现了优先级函数为P/T即每分钟减小分数除需要的做题时间

排好序之后,进行01背包的dp算法

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
int N,T;
const int maxn=55;
const int maxt=1e5+5;
ll dp[maxt];
struct node
{
    int maxPoints;//最大分数
    int pointsPerMinute;//每分钟减小的分数
    int requiredTime;//需要的时间
}question[maxn];//存储问题信息
bool cmp(struct node& a,struct node& b) {
        return a.pointsPerMinute*1.0/a.requiredTime >= b.pointsPerMinute*1.0/b.requiredTime;
    }
int main()
{
    cin>>N>>T;
    for(int i=0;i<N;i++) cin>>question[i].maxPoints;
    for(int i=0;i<N;i++) cin>>question[i].pointsPerMinute;
    for(int i=0;i<N;i++) cin>>question[i].requiredTime;
    sort(question,question+N,cmp);
    ll ans=0;
    for(int i=0;i<N;i++){
        for(int j=T;j>=question[i].requiredTime;j--){
            dp[j]=max(dp[j],dp[j-question[i].requiredTime]+question[i].maxPoints-question[i].pointsPerMinute*j);
            ans=max(dp[j],ans);
        }
    }
    cout<<ans<<endl;


}

 

 

 

举报

相关推荐

0 条评论