2021-05-15

ACM学习总结

这周继续做背包的题目,而且学习了二分查找的知识。
二分查找是在单调有序中寻找元素,集合有上下界,每次将所需元素将集合中间的值比较,若所需值大于中值,则将中值赋给下限,若小于中值,则将则将中值赋给上限,直到找到所需值。

做的题目

The aspiring Roy the Robber has seen a lot of American movies, and knows that the bad guys usually gets caught in the end, often because they become too greedy. He has decided to work in the lucrative business of bank robbery only for a short while, before retiring to a comfortable job at a university.
For a few months now, Roy has been assessing the security of various banks and the amount of cash they hold. He wants to make a calculated risk, and grab as much money as possible.
His mother, Ola, has decided upon a tolerable probability of getting caught. She feels that he is safe enough if the banks he robs together give a probability less than this.
Input
The first line of input gives T, the number of cases. For each scenario, the first line of input gives a floating point number P, the probability Roy needs to be below, and an integer N, the number of banks he has plans for. Then follow N lines, where line j gives an integer Mj and a floating point number Pj .
Bank j contains Mj millions, and the probability of getting caught from robbing it is Pj .
Output
For each test case, output a line with the maximum number of millions he can expect to get while the probability of getting caught is less than the limit set.
题意:一个小偷偷银行,每个银行都有一定钱数,但每个银行偷时都有一定的被捕概率,给出每个银行的钱数和小偷被捕概率,给出总被捕概率(小偷偷完偷银行后被捕概率不得超过总被捕概率),求小偷所能获得的最大金钱数。
这个题刚上来理解错了,将背包容量理解成了背包容量,以为所有偷的银行的被捕概率相加不能超过总被捕获概率,后来发现错了,应该是乘。乘的话,将每个被捕概率都用1减,则得出每个银行的逃脱概率和总逃脱概率。所偷银行的逃脱概率相册和那个所得的逃脱概率不得小于总逃脱概率。而且概率是小数,数组下标不可能是小数,所以不能将概率看做背包容量。
将钱数看做背包容量,求不同个数银行获得一定钱数的逃脱概率,方程为dp[j]=max(dp[j],dp[j-a[i]]*(1-b[i]));然后从获得金钱大的开始遍历将逃脱概率与总逃脱概率比较,若满足条件,则找出结果。
代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=5000;
int a[N];
double dp[20000],b[N];

int main()
{
    int n,t;
    double m;
    scanf("%d",&t);
    while(t--)
    {
        int sum=0;
        scanf("%lf%d",&m,&n);
        for(int i=1;i<=n;++i)
        {
            scanf("%d%lf",&a[i],&b[i]);
            sum+=a[i];
        }
        memset(dp,0,sizeof(dp));
        dp[0]=1.0;
        for(int i=1;i<=n;++i)
        {
            for(int j=sum;j>=a[i];--j)
                dp[j]=max(dp[j],dp[j-a[i]]*(1-b[i]));
        }

        for(int i=sum;i>=0;--i)
        {
            if(dp[i]>=(1-m))
            {
                printf("%d\n",i);
                break;
            }
        }
    }
    return 0;

}

Suppose there are 5 types of coins: 50-cent, 25-cent, 10-cent, 5-cent, and 1-cent. We want to make
changes with these coins for a given amount of money.
For example, if we have 11 cents, then we can make changes with one 10-cent coin and one 1-cent
coin, two 5-cent coins and one 1-cent coin, one 5-cent coin and six 1-cent coins, or eleven 1-cent coins.
So there are four ways of making changes for 11 cents with the above coins. Note that we count that
there is one way of making change for zero cent.
Write a program to find the total number of different ways of making changes for any amount of
money in cents. Your program should be able to handle up to 7489 cents.
题意:
给出五种货币,给出一个钱数,求能用五种货币组合成所给钱数的种数。
每种货币的数目不限,为完全背包问题。
设数组dp[i][j]表示用前i种金币组成金钱j的种数,那么用前i种金币组成金钱j的种数,就等于只用前i-1种金币组成金钱j的种数(前i种金币可以不全用)加上用上第i种金币组成金钱j的种数,即dp方程为dp[i][j]=dp[i][j-v[i]]+dp[i-1][j];
代码

#include<bits/stdc++.h>
using namespace std;
int v[6];
int dp[1001][1001];
int main()
{
    int n;
    v[1]=1,v[2]=5,v[3]=10,v[4]=25,v[5]=50;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=5;i++)
        dp[i][0]=1;
         for(int i=1;i<=5;i++)
            dp[i-1][1]=0;
        for(int i=1;i<=5;i++)
            for(int j=1;j<=n;j++)
               {dp[i][j]=dp[i][j-v[i]]+dp[i-1][j];}

        cout <<dp[5][n]<<endl;

    }
    return 0;
}

优化后的代码

#include<iostream>


using namespace std;
int v[6];
int dp[100001];
int main()
{
    int n;
    v[0]=1,v[1]=5,v[2]=10,v[3]=25,v[4]=50;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof dp);
        dp[0]=1;
        for(int i=0;i<5;i++)
            for(int j=v[i];j<=n;j++)
                dp[j]=dp[j-v[i]]+dp[j];
        cout <<dp[n]<<endl;

    }
    return 0;
}

New Zealand currency consists of $100, $50, $20, $10, and $5 notes and $2, $1, 50c, 20c, 10c and 5c
coins. Write a program that will determine, for any given amount, in how many ways that amount
may be made up. Changing the order of listing does not increase the count. Thus 20c may be made
up in 4 ways: 1×20c, 2×10c, 10c+2×5c, and 4×5c.
这道题和上面那道差不多,区别是货币种类数不同,需要注意的是精度及数的类型问题,因为开始时输入的是多少美元,为
实型数,而遍历时化为分遍历,需进行数类型转换。这里int mm=(n*100+0.5)表示四舍五入。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int m[15]={10000,5000,2000,1000,500,200,100,50,20,10,5};
double n;
int dp[10001];
int main()
{
 while(scanf("%lf",&n)==1)
 {

  int mm=(n*100+0.5);
  memset(dp,0,sizeof(dp));
  dp[0]=1;
  for(int i=0;i<11;i++)
   for(int j=m[i];j<=mm;j++)
   {

     dp[j]+=dp[j-m[i]];
    }

  cout<<dp[mm]<<endl;
 }
 return 0;
}

收获与感悟

1.四舍五入int i = (int)(f + 0.5);
2.二分法求“将n根网线切成k段相同长度的网线”在这类问题时,若找的值和所需值相等(不一定为最后结果),按值大于所需值处理,将中值赋给下限。
这周背包问题告一段落,但对背包问题及动态规划的理解还需深入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值