[DP]Exercise week11 A~F

目录

  • A.[水]买房子
    • 题意
    • 样例
      • 样例输入:
      • 样例输出:
    • 思路
    • 总结
    • 代码
  • B.[模拟]排方阵
    • 题意
    • 样例
      • 样例输入:
      • 样例输出:
    • 思路
    • 总结
    • 代码
  • C.[水]解密
    • 题意
    • 样例
      • 样例输入:
      • 样例输出:
    • 思路
    • 总结
    • 代码
  • D.[模拟]恰饭
    • 题意
    • 样例
      • 样例输入:
      • 样例输出:
    • 思路
    • 总结
    • 代码
  • B.[多重背包]ATM
    • 题意
    • 样例
      • 样例输入:
      • 样例输出:
    • 思路
    • 总结
    • 代码
  • F.[枚举]开车
    • 题意
    • 样例
      • 样例输入:
      • 样例输出:
    • 思路
    • 总结
    • 代码

A.[水]买房子

题意

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

样例

样例输入:

一行,包含两个正整数 N(10 \le N \le 50)N(10≤N≤50),K(1 \le K \le 20)K(1≤K≤20),中间用单个空格隔开。

50 10

样例输出:

如果在第 2020 年或者之前就能买下这套房子,则输出一个整数 MM,表示最早需要在第 MM 年能买下;否则输出"Impossible"。

8

思路

用一个while循环模拟 记得开double
20年及以内就输出年数 否则输出impossible并break


总结

好(shui)题


代码

#include
#include
#include
#include
#include
using namespace std;
int n,k;
int year;
double cur;
int main()
{
    cin>>n>>k;
    cur=200;
    while(++year)
    {
        if(n*year>=cur) {cout<<year<<endl;break;}
        cur*=(1+k*0.01);

        if(year>20) {cout<<"Impossible"<<endl;break;}
    }

    system("pause");
    return 0;
}

B.[模拟]排方阵

题意

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

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

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

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

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

1.不需要旋转则输出 0

2.顺时针旋转 90° 则输出 1

3.顺时针旋转 180° 则输出 2

4.顺时针旋转 270° 则输出 3

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

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

样例

样例输入:

第一行为一个整数 n(1<=n<=20)

接下来的 n 行同学们已经列好的 01 方阵;

再接下来的 n 行表示老师希望的的 01 方阵。

4
0 0 0 0
0 0 0 0
0 1 0 0
0 0 0 0
0 0 0 0
0 1 0 0
0 0 0 0
0 0 0 0

样例输出:

输出仅有一行,该行只有一个整数,如题所示。

1

思路

1.用一个函数check()来判断当前的临时阵型是否已经和目标阵型一致

2.用一个函数Clockwise()来进行一次顺时针旋转90度的操作

3.每次旋转完后用check()进行判断 一致就输出 若旋转了3次仍不一致 则输出"-1"


总结

简单的模拟即可


代码

#include
#include
#include
#include
#include
using namespace std;
int n,st[30][30],ed[30][30];
int tmp[30][30];
bool check()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(tmp[i][j]!=ed[i][j])  return false;
        
    return true;
}
void Clockwise()
{
    for(int i=1,tmpx=1;i<=n;i++,tmpx++)
    {
        for(int j=n,tmpy=1;j>=1;j--,tmpy++)    tmp[tmpx][tmpy]=st[j][i];
    }

    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)   st[i][j]=tmp[i][j];
}
void print()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)   cout<<tmp[i][j]<<" ";
        cout<<endl;
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)   {cin>>st[i][j];tmp[i][j]=st[i][j];}
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)   cin>>ed[i][j];



    if(check()) {cout<<"0"<<endl;exit(0);}
    
    Clockwise();
    if(check()) {cout<<"1"<<endl;exit(0);}

    Clockwise();
    if(check()) {cout<<"2"<<endl;exit(0);}

    Clockwise();
    if(check()) {cout<<"3"<<endl;exit(0);}

    cout<<"-1"<<endl;
    system("pause");
    return 0;
}

C.[水]解密

题意

Julius Caesar 曾经使用过一种很简单的密码。对于明文中的每个字符,将它用它字母表中后 55 位对应的字符来代替,这样就得到了密文。比如字符’A’用’F’来代替。如下是密文和明文中字符的对应关系。

密文\text{A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

明文\text{V W X Y Z A B C D E F G H I J K L M N O P Q R S T U}V W X Y Z A B C D E F G H I J K L M N O P Q R S T U

你的任务是对给定的密文进行解密得到明文。

你需要注意的是,密文中出现的字母都是大写字母。密文中也包括非字母的字符,对这些字符不用进行解码。

样例

样例输入:

一行,给出密文,密文不为空,而且其中的字符数不超过 200200。

NS BFW, JAJSYX TK NRUTWYFSHJ FWJ YMJ WJXZQY TK YWNANFQ HFZXJX

样例输出:

输出一行,即密文对应的明文。

IN WAR, EVENTS OF IMPORTANCE ARE THE RESULT OF TRIVIAL CAUSES

思路

读入之后 对字符串进行遍历 进行如下操作:
①.如果当前字符是大写字母 即 'A'<=s[i]<='Z' 则转化为s[i]-5+26(注意取模)
②.如果不是大写字母 则直接跳过
随后输出字符串


总结

好(水)题


代码

#include
#include
#include
#include
#include
using namespace std;
string s;
void decode()
{
    for(int i=0;i<s.length();i++)
    {
        if(s[i]<'A'||s[i]>'Z')  continue;

        s[i]=(s[i]-'A'-5+26)%26;
        s[i]+='A';
    }
}
int main()
{
    
    getline(cin,s);
    decode();
    cout<<s<<endl;
    system("pause");
    return 0;
}

D.[模拟]恰饭

题意

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

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

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

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

样例

样例输入:

第一行:一个整数n(2≤n≤100000),寿司序列的长度。
第二行:n个整数(每个整数不是1就是2,意义如上所述)

9
2 2 1 1 1 2 2 2 2

样例输出:

一个整数(代表东东可以选择的最长的一段连续的且合法的寿司)

6

思路

1.首先我们看到数据范围是106,故考虑一个O(n)的算法,即通过从前往后遍历就可以求解的算法

2.从前向后遍历 用变量cur记录当前的寿司种类 然后向后查找 每次遇到与当前寿司种类相同的 tnt[cur]+1

3.如果寿司种类发生了改变 则尝试更新ans值 即 ans=max(ans,2*min(tnt[1],tnt[2])) 因为根据题目要求 还需要这两种寿司的数目一样 故取min

4.随后将tnt[a[i]]初始化为1 并把cur置a[i]

(记得出for循环后还要尝试更新ans的值


总结

一个简单的模拟题 不过需要一点点思考量


代码

#include
#include
#include
#include
#include
using namespace std;
int n;
int a[100010];
int tnt[3];
int cur;
int ans=-1;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)   scanf("%d",&a[i]);

    cur=a[1];
    for(int i=1;i<=n;i++)
    {
        //记录当前这一块的长度
        if(a[i]==cur)
        {
            tnt[a[i]]++;
            continue;
        }

        //如果到了交界处 
        ans=max(ans,min(tnt[1],tnt[2])*2);

        tnt[a[i]]=1;
        cur=a[i];
    }

    ans=max(ans,min(tnt[1],tnt[2])*2);
    cout<<ans<<endl;
    system("pause");
    return 0;
}

B.[多重背包]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 的程序,可根据具体金额请求机器交付现金。
注意,这个程序计算程序得出的最大现金少于或等于可以根据设备的可用票据供应有效交付的现金。

样例

样例输入:

程序输入来自标准输入。 输入中的每个数据集代表特定交易,其格式为:Cash N n1 D1 n2 D2 … nN DN其中0 <= Cash <= 100000是所请求的现金量,0 <= N <= 10是 纸币面额的数量,0 <= nk <= 1000是Dk面额的可用纸币的数量,1 <= Dk <= 1000,k = 1,N。 输入中的数字之间可以自由出现空格。 输入数据正确。

735 3  4 125  6 5  3 350
633 4  500 30  6 100  1 5  0 1
735 0
0 3  10 100  10 50  10 10

样例输出:

对于每组数据,程序将在下一行中将结果打印到单独一行上的标准输出中。

735
630
0
0

思路

1.先分析一下题意:
要求输出小于等于Cash的最大值 且 每种面额的钞票有Ci个 所以可见是一个多重背包问题

2.需要注意一点的是 这里的花费w[i]和价值v[i]其实是同一个 即钞票的面额

3.所以我们只需要按照常规多重背包的思路 先进行二进制拆分 再对拆分后的物品跑一遍0/1背包即可


总结

很好的一个题 既有一点点变种 也考察了多重背包的二进制拆分以及01背包的DP板子


代码

#include
#include
#include
#include
#include
#define MAXN 100100
using namespace std;
//多重背包  
//价值 == 花费
int V,n;
int v[20],c[20];
int vv[MAXN],cnt;
int dp[MAXN];
void change()//二进制拆分
{
    for(int i=1;i<=n;i++)
    {
        int now=c[i];
        for(int bit=1;bit<=now;bit<<=1)
        {
            vv[++cnt]=bit*v[i];
            now-=bit;
        }

        if(now>0)
        {
            vv[++cnt]=now*v[i];
        }
    }
}
int main()
{
    while(cin>>V>>n)
    {
        for(int i=1;i<=n;i++)   vv[i]=0;
        cnt=0;
        for(int i=1;i<=n;i++)   cin>>c[i]>>v[i];
        change();

        for(int i=1;i<=V;i++) dp[i]=0;

        for(int i=1;i<=cnt;i++)
            for(int j=V;j>=vv[i];j--)
                dp[j]=max(dp[j],dp[j-vv[i]]+vv[i]);
    
        cout<<dp[V]<<endl;
    }
    system("pause");
    return 0;
}

F.[枚举]开车

题意

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

假设:

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

样例

样例输入:

多组输入

每行输入第一个数字N, 代表总时间,第二个数字 M 代表有 M 张唱片,后面紧跟 M 个数字,代表每张唱片的时长 例如样例一: N=5, M=3, 第一张唱片为 1 分钟, 第二张唱片 3 分钟, 第三张 4 分钟

所有数据均满足以下条件:

N≤10000
M≤20

5 3 1 3 4
10 4 9 8 4 2
20 4 10 5 7 4
90 8 10 23 1 2 3 4 5 7
45 8 4 10 44 43 12 9 8 2

样例输出:

输出所有唱片的时长和总时长,具体输出格式见样例

1 4 sum:5
8 2 sum:10
10 5 4 sum:19
10 23 1 2 3 4 5 7 sum:55
4 10 12 9 8 2 sum:45

思路

1,看到数据范围 心中窃喜 O(2^20) 大概是可以接受的 (/斜眼笑

2.尝试着枚举每一个唱片选与不选 并且维护一个ans值 表示每种方案能到达的最大时长(小于总时长)
并且记录方案 在更新ans时同时更新方案

3.然后就惊讶的发现自己AC了


总结

由于数据范围 可以直接通过穷举求解
不过不知道如何动态规划同时记录方案呢(TAT


代码

#include
#include
#include
#include
#include
#define MAXN 100100
#define INF 0x7ffffff
using namespace std;
//?
int V,n,ans;
int t[30],tmp[30],path[30];
void dfs(int step,bool chosen,int sum)//第几个 是否选 当前总和
{
    if(chosen==1)   tmp[step]=t[step];
    else tmp[step]=-INF;

    if(sum>V) return;
    if(step==n)
    {
        if(sum<=ans) return;
        ans=sum;
        for(int i=1;i<=step;i++)    path[i]=tmp[i];
        return ;
    }

    dfs(step+1,1,sum+t[step+1]);
    dfs(step+1,0,sum);
}
int main()
{
    while(cin>>V>>n)
    {
        ans=0;
        for(int i=1;i<=n;i++)   cin>>t[i];
        dfs(1,1,t[1]);
        dfs(1,0,0);
        for(int i=1;i<=n;i++)   
            if(path[i]!=-INF)    cout<<path[i]<<" ";
        cout<<"sum:"<<ans<<endl;
    }
    
    
    system("pause");
    return 0;
}

你可能感兴趣的:(csp,算法,动态规划)