贪心-国王的游戏(大数乘除)、连接子串

目录

 

国王的游戏:

题目:

思路:

大数据高精度乘除模板:

乘:

除:模拟除法过程

代码:

连接子串

题目:

思路:

代码:​​​​​​​

国王的游戏:

题目:

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

 

对于 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 。

思路:

尽可能少,确定贪心策略,发现两个大臣互换位置对其它大臣获得奖赏数量没有影响,比较下换位置前和换位置后哪一个最大的较小。

前k个大臣左手乘积为L,第k+1个大臣L1,R1,第k+2个大臣L2,R2;假设让第k+1个大臣在前面更适合,当他在前时,最大值为max(L/R1①,L*L1/R2②),当他在后面时,最大值为max(L/R2③,L*L2/R1④);让他在前面更合适,那么就要保证前者的max小于后者的max,显然①<④,③<②,那么后者肯定要选择④(如果选②必定比前者小,因为他小于③)。这样只要能保证②<④即可,化简得L1*R1<L2*R2。由此可得,对于任意两个大臣,左右手数相乘小的靠前获得的最大值最小。

         数据范围感人,10000个大臣,每个大人最多10000,那就是10000^10000。接下来看下高精度大数乘除:

数组存储大数据乘法,可以理解为把每一位拆分为(xn*10^n……..x1*10+x0*1)*x对每一位乘,一旦系数大于10,就可以进位了,注意的是,每一位都要仅为,因为每一位数都可能大于9。所以至少循环len次。

大数据高精度乘除模板:

乘:

void mul(ll x){

         for(int i = 1;i <= len;i++)

                  num[i] = num[i]*x;

         int cnt = 1;

         while(num[cnt] > 9||cnt <= len){//个位无进位不代表其他位无进位

                  num[cnt+1] += num[cnt]/10;

                  num[cnt]%=10;

                  cnt++;

         }

         len=cnt;

         if(!num[len])      len--;

         return ;

}

除:模拟除法过程

void div(ll x){

         for(int i = len;i >= 1;i--){

                  num[i-1] += (num[i]%x)*10;

                  num[i]/=x;

         }

         while(!num[len]) len--;

}

代码:

#include<algorithm>

#include<iostream>

#include<cstring>

#include<string>

#include<cstdlib>

#include<map>

#include<cmath>

#include<vector>



using namespace std;

typedef long long  ll;

const int maxn = 1e5+50;

typedef struct{

         ll l;

         ll r;

         ll z;

}hand;

hand arr[maxn];

int len,n;

ll num[maxn];//最多1e5个1e4相乘 也就是4e5+1位数

bool cmp(hand a,hand b){

         return a.z < b.z;//奇葩 等号竟然没过。

}

void mul(ll x){

         for(int i = 1;i <= len;i++)

                  num[i] = num[i]*x;

         int cnt = 1;

         while(num[cnt] > 9||cnt <= len){//个位无进位不代表其他位无进位

                  num[cnt+1] += num[cnt]/10;

                  num[cnt]%=10;

                  cnt++;

         }

         len=cnt;

         if(!num[len])      len--;

         return ;

}

void div(ll x){

         for(int i = len;i >= 1;i--){

                  num[i-1] += (num[i]%x)*10;

                  num[i]/=x;

         }

         while(!num[len]) len--;

}

int main(){

         cin >> n;

         cin >> arr[0].l >> arr[0].r;

         for(int i = 1;i <= n;i++){

                  cin >> arr[i].l >> arr[i].r;

                  arr[i].z = arr[i].l*arr[i].r;

         }

         sort(arr+1,arr+1+n,cmp);

         len=1;num[1]=1;

         for(int i = 0;i<n;i++)    mul(arr[i].l);

         div(arr[n].r);

         for(int i = len;i >= 1;i--)        cout<<num[i];

         if (!len) printf("1");

         return 0;

}

连接子串

题目:

设有n个正整数(n ≤ 20),将它们联接成一排,组成一个最大的多位整数。

例如:n=3时,3个整数13,312,343联接成的最大整数为:34331213

又如:n=4时,4个整数7,13,4,246联接成的最大整数为:7424613

输入描述:

第一行,一个正整数n。

第二行,n个正整数。

输出描述:

一个正整数,表示最大的整数

思路:

不可以直接判断字典序:23 2323231 or 33  331  9错误,如果想着判断这些特例就会很麻烦。

同样,任何相邻的两个子串交换不会影响其它子串的贡献,就看一下互换前后哪一种最大。

现在感觉在冒泡。最后只要能达到任何两个相邻子串互换都不如不换,就是最大字典序了。

假设对子串s1…sm…sn….. 如果sm和sn交换更好的话,sm+1在sn前面又更好些,sm+2在sn前面又更好些(因为之前肯定比较过了在sn前面更好)……最后又回到了原来的位置。

         冒泡复杂度太高,冒泡就是排序,不如直接快排,sort函数cmp一下就可以了。

bool cmp(string a,string b){

         return a+b>b+a;

}

代码:

#include<algorithm>

#include<iostream>

#include<cstring>

#include<string>

#include<cstdlib>

#include<map>

#include<cmath>

#include<vector>

//思路:....AB...若想让序列最大 保证每一对相邻的A+B>B+A即可,转化为排序。

//不可以直接判断字典序:23 2323231 or 33  331  9错误

using namespace std;

typedef long long ll;

const int maxn = 1e6+50;

string s[30];

string ans;

bool cmp(string a,string b){

            return a+b>b+a;

}

int main(){

            int n;

            cin >> n;

            for(int i = 0;i < n;i++)        cin>>s[i];

            sort(s,s+n,cmp);

            for(int i = 0;i < n;i++)        ans+=s[i];

            cout << ans;

            return 0;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用RISC-V指令集汇编语言实现大数计算乘法和除法的代码示例: 大数乘法: ``` # r1-r3存放乘数,r4-r6存放被乘数,r7-r9存放结果 # r10-r12作为计算过程中的临时寄存器 mul: addi sp, sp, -12 # 为临时寄存器分配空间 sw ra, 8(sp) sw s0, 4(sp) sw s1, 0(sp) li s0, 0 # s0作为计算过程中的进位标志 li s1, 0 # s1作为累加器 # 逐位相乘并累加 loop: lbu t0, 0(r1) lbu t1, 0(r4) mul t2, t0, t1 add t2, t2, s0 add s0, zero, t2 lbu t0, 1(r1) lbu t1, 1(r4) mul t2, t0, t1 add t2, t2, s0 srl s0, t2, 8 add t2, t2, s1 add s1, zero, t2 lbu t0, 2(r1) lbu t1, 2(r4) mul t2, t0, t1 add t2, t2, s0 srl s0, t2, 8 add t2, t2, s1 add s1, zero, t2 addi r1, r1, 3 addi r4, r4, 3 bne r1, zero, loop # 将结果存放到r7-r9中 sb s1, 0(r7) sb s0, 1(r7) sb zero, 2(r7) lw ra, 8(sp) lw s0, 4(sp) lw s1, 0(sp) addi sp, sp, 12 ret ``` 大数除法: ``` # r1-r2存放被除数,r3-r4存放除数,r5-r6存放商,r7-r8存放余数 # r9-r12作为计算过程中的临时寄存器 div: addi sp, sp, -12 # 为临时寄存器分配空间 sw ra, 8(sp) sw s0, 4(sp) sw s1, 0(sp) # 初始化商和余数 li s0, 0 # s0作为计算过程中的临时寄存器 li s1, 0 # s1作为商 li t0, 0 # t0作为余数 li t1, 0 # t1作为除数 # 计算除数的位数 addi t2, zero, 32 clz t1, t1 sub t2, t2, t1 # 逐位进行除法运算 loop: sll t0, t0, 1 sll s0, s0, 1 lbu t1, 0(r1) addi r1, r1, 1 srl t0, t0, t2 or t0, t0, t1 bge t0, r3, divstep j loop # 计算商和余数 divstep: sub t0, t0, r3 ori s0, s0, 1 blt t0, r3, loop sub t0, t0, r3 ori s0, s0, 1 blt t0, r3, loop # 将商和余数存放到r5-r6和r7-r8中 addi t0, t0, -1 sb s0, 0(r5) sb zero, 1(r5) sb t0, 2(r5) sb zero, 0(r6) sb zero, 1(r6) sb zero, 2(r6) sb t0, 0(r7) sb zero, 1(r7) sb zero, 2(r7) sb zero, 0(r8) sb zero, 1(r8) sb zero, 2(r8) lw ra, 8(sp) lw s0, 4(sp) lw s1, 0(sp) addi sp, sp, 12 ret ``` 请注意,这些代码只是示例,可能需要根据具体情况进行修改和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值