【20200429程序设计思维与实践 Week11 作业】


A - 必做题11-1

题意

蒜头君从现在开始工作,年薪 N 万。他希望在蒜厂附近买一套 60 平米的房子,现在价格是 200 万。假设房子价格以每年百分之 K 增长,并且蒜头君未来年薪不变,且不吃不喝,不用交税,每年所得 N万全都积攒起来,问第几年能够买下这套房子?(第一年年薪 N 万,房价 200 万)


思路

本题用一个循环来代表时间推移。每次循环中蒜头君多一份工资,房价增长一次。

跳出循环的条件有三种:工资总数大于当前房价、房子这一年的涨价已超过每年工资、年份超过20。


代码

#include<iostream>
using namespace std;

double N,K;

int main(){
    cin>>N>>K;
    K/=100;
    double price=200;
    bool jdg=0;
    int cot=1;
    while(cot<=20){
        if(N*cot>=price){
            jdg=1;
            break;
        }
        cot++;
        price*=(K+1);
    }
    if(jdg)cout<<cot;
    else cout<<"Impossible";
}

B - 必做题11-2

题意

蒜头君的班级里有 n^2个同学,现在全班同学已经排列成一个n∗n 的方阵,但是老师却临时给出了一组新的列队方案

为了方便列队,所以老师只关注这个方阵中同学的性别,不看具体的人是谁

这里我们用 0 表示男生,用 1 表示女生

现在蒜头君告诉你同学们已经排好的方阵是什么样的,再告诉你老师希望的方阵是什么样的

他想知道同学们已经列好的方阵能否通过顺时针旋转变成老师希望的方阵

不需要旋转则输出 0

顺时针旋转 90° 则输出 1

顺时针旋转 180° 则输出 2

顺时针旋转 270° 则输出 3

若不满足以上四种情况则输出 -1

若满足多种情况,则输出较小的数字


思路

本题只要进行四次判断即可。

不需要旋转只要将原队形和目标队形比对一遍即可;

顺时针旋转 90° 只要判断ori[i][j]!=aim[j][n-i+1]是否全部满足;

顺时针旋转 180° 只要判断ori[i][j]!=aim[n-i+1][n-j+1]是否全部满足;

顺时针旋转 270° 只要判断ori[i][j]!=aim[n-j+1][i]是否全部满足。


代码

#include<iostream>
using namespace std;

int ori[25][25],aim[25][25];
int n,ans;

int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>ori[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>aim[i][j];
    ans=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(ori[i][j]!=aim[i][j]){
                ans=-1;
                break;
            }
        }
    }
    if(ans==-1){
        ans=1;
        for(int i=1;i<=n;i++){
           for(int j=1;j<=n;j++){
                if(ori[i][j]!=aim[j][n-i+1]){
                    ans=-1;
                    break;
                }
            }
        }
    }
    if(ans==-1){
        ans=2;
        for(int i=1;i<=n;i++){
           for(int j=1;j<=n;j++){
                if(ori[i][j]!=aim[n-i+1][n-j+1]){
                    ans=-1;
                    break;
                }
            }
        }
    }
    if(ans==-1){
        ans=3;
        for(int i=1;i<=n;i++){
           for(int j=1;j<=n;j++){
                if(ori[i][j]!=aim[n-j+1][i]){
                    ans=-1;
                    break;
                }
            }
        }
    }
    cout<<ans;
}


C - 必做题11-3

题意

Julius Caesar 曾经使用过一种很简单的密码。对于明文中的每个字符,将它用它字母表中后 5 位对应的字符来代替,这样就得到了密文。你的任务是对给定的密文进行解密得到明文。你需要注意的是,密文中出现的字母都是大写字母。密文中也包括非字母的字符,对这些字符不用进行解码。


思路

对大写字母字符对应的ASCII码,利用5位位移和取余操作将密文解为明文即可。

利用字符数组接收输入,只对大写字母元素进行上述操作,最后输出。

debug:对ASCII码进行减法运算得到负数时,直接对其取余会得到乱码,因此利用模26将减5改为加21。


代码

#include<iostream>
using namespace std;

string str;
char word[205];
int len;

int main(){
    getline(cin,str);
    len=str.length();
    for(int i=0;i<len;i++){
        if(str[i]<'A'||str[i]>'Z')word[i]=str[i];
        else{
            char sp=str[i];
            sp-='A';
            sp+=21;
            sp%=26;
            word[i]=sp+'A';
        }
    }
    for(int i=0;i<len;i++)cout<<word[i];
}

D - 必做题11-4

题意

东东和他的女朋友(幻想的)去寿司店吃晚餐(在梦中),他发现了一个有趣的事情,这家餐厅提供的 n 个的寿司被连续的放置在桌子上 (有序),东东可以选择一段连续的寿司来吃

东东想吃鳗鱼,但是东妹想吃金枪鱼。核 平 起 见,他们想选择一段连续的寿司(这段寿司必须满足金枪鱼的数量等于鳗鱼的数量,且前一半全是一种,后一半全是另外一种)我们用1代表鳗鱼,2代表金枪鱼。

比如,[2,2,2,1,1,1]这段序列是合法的,[1,2,1,2,1,2]是非法的。因为它不满足第二个要求。

东东希望你能帮助他找到最长的一段合法寿司,以便自己能吃饱。


思路

此处用到了求最大直方的做法。

对数据预处理,记下每段连续相同值的长度并记录进一个数组。显然这个数组表示的元素为鳗鱼和金枪鱼段长度的交替出现。要取一段最长合法寿司,就是在这个“直方图”中找到底长为2的最大直方。一次扫描即可求得。


代码

#include<iostream>
using namespace std;

int n,cnt;
int a[100005],b[100005],c[100005];

int main(){
    cin>>n;
    for(int i=0;i<n;i++)cin>>a[i];
    int temp=0;
    temp++;
    for(int i=1;i<n;i++){
        if(a[i]!=a[i-1]){
            b[cnt++]=temp;
            temp=0;
        }
        temp++;
    }
    b[cnt++]=temp;
    if(b[1]<=b[0])c[0]=2*b[1];
    else c[0]=2*b[0];
    for(int i=1;i<cnt-1;i++){
        int t=b[i-1]>b[i+1]?b[i-1]:b[i+1];
        if(t<=b[i])c[i]=2*t;
        else c[i]=2*b[i];
    }
    if(b[cnt-2]<=b[cnt-1])c[cnt-1]=2*b[cnt-2];
    else c[cnt-1]=2*b[cnt-1];
    int ans=c[0];
    for(int i=1;i<cnt;i++)
        if(c[i]>ans)ans=c[i];
    cout<<ans;
}


E - 选做题11-1 东东与 ATM

题意

一家银行计划安装一台用于提取现金的机器。
机器能够按要求的现金量发送适当的账单。
机器使用正好N种不同的面额钞票,例如D_k,k = 1,2,…,N,并且对于每种面额D_k,机器都有n_k张钞票。
例如,
N = 3,
n_1 = 10,D_1 = 100,
n_2 = 4,D_2 = 50,
n_3 = 5,D_3 = 10
表示机器有10张面额为100的钞票、4张面额为50的钞票、5张面额为10的钞票。
东东在写一个 ATM 的程序,可根据具体金额请求机器交付现金。
注意,这个程序计算程序得出的最大现金少于或等于可以根据设备的可用票据供应有效交付的现金。


思路

本题是多重背包问题习题。

首先对多重集即同面值钞票用二进制拆分预处理。新的“物品”集即拆分后的集合,转变回01背包问题。

这个问题的变化之处在于物品的体积和价值是相等的,略微修改标准01背包问题的状态转移方程即可。

检查数据规模,容积Cash<=100000,又发现nk<=1000,对其进行二进制拆分后二维数组会MLE,所以还要用到滚动数组的技巧。


代码

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int Cash,N;
int n[15];
int D[15];
int f[100005];

int main(){
    while(cin>>Cash){
        cin>>N;
        for(int i=1;i<=N;i++)
            cin>>n[i]>>D[i];
        vector<int> v;
        for(int i=1;i<=N;i++){
            if(n[i]>1){
                int k=1;
                int temp=n[i];
                while(temp>k){
                    v.push_back(k*D[i]);
                    temp-=k;
                    k*=2;
                }
                v.push_back(temp*D[i]);
            }else if(n[i]==1){
                v.push_back(D[i]);
            }
        }
        N=v.size();
        int* w=new int[N+5];
        for(int i=1;i<=N;i++){
            w[i]=v[i-1];
//            cout<<w[i]<<endl;
        }
        for(int i=0;i<=Cash;i++)f[i]=0;
        for(int i=1;i<=N;i++){
            for(int j=Cash;j>=0;j--){
                if(j-w[i]>=0){
                    f[j]=max(f[j],f[j-w[i]]+w[i]);
                }
            }
        }
        cout<<f[Cash]<<endl;
        delete w;
    }
}

F - 选做题11-2 东东开车了

题意

东东开车出去泡妞(在梦中),车内提供了 n 张CD唱片,已知东东开车的时间是 n 分钟,他该如何去选择唱片去消磨这无聊的时间呢

假设:

CD数量不超过20张
没有一张CD唱片超过 N 分钟
每张唱片只能听一次
唱片的播放长度为整数
N 也是整数
我们需要找到最能消磨时间的唱片数量,并按使用顺序输出答案(必须是听完唱片,不能有唱片没听完却到了下车时间的情况发生)


思路

本题也是一个背包问题,并且和上一题一样,物品的价值和体积相等(都是时间)。

本题要求输出组成最优解的各个元素,所以必须要记录动态规划过程。采取的方法是记录每一次状态转移的“父状态”。被记录下的信息为父状态在二维数组中的行列索引,这样,输出时只要将当前状态与父状态相减,就可以知道新加入的元素值,并且便于回溯。

这里debug遇到一个问题:将变量命名为time在vj上会导致编译错误?


代码

#include<iostream>
#include<algorithm>
using namespace std;

int N,M;
int t[25];//time是什么特殊保留关键词嘛?
int f[25][10005];
pair<int,int> p[25][10005];

int sto[10000+5];

int main(){
while(cin>>N){
    cin>>M;
    for(int i=1;i<=M;i++){
        int sp;
        cin>>sp;
        t[i]=sp;
    }
    for(int i=0;i<=N;i++){
        f[0][i]=0;
        p[0][i].first=-1;
        p[0][i].second=-1;
    }
    for(int i=1;i<=M;i++){
        for(int j=0;j<=N;j++){
            f[i][j]=f[i-1][j];
            p[i][j].first=i-1;
            p[i][j].second=j;
            if(j-t[i]>=0){
                if(f[i-1][j-t[i]]+t[i]>f[i][j]){
                    f[i][j]=f[i-1][j-t[i]]+t[i];
                    p[i][j].first=i-1;
                    p[i][j].second=j-t[i];
                }
            }
        }
    }
    int ans=f[M][N];
    int scnt=0;
    pair<int,int> temp1,temp2;
    temp1.first=M;
    temp1.second=N;
    while(temp1.first!=-1){
        temp2=p[temp1.first][temp1.second];
        if(temp2.first!=-1)sto[++scnt]=f[temp1.first][temp1.second]-f[temp2.first][temp2.second];
        else sto[++scnt]=0;
        temp1=temp2;
    }
    for(int i=scnt;i>0;i--){
        if(sto[i]!=0)cout<<sto[i]<<" ";
    }
    cout<<"sum:"<<ans<<endl;
}
//    system("pause");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值