week2

A - Last minute enhancements

Last minute enhancements

思路

连续一样的音符最多只能变一个,因为就算多变了几个,变了之后的这几个还是一样的,有效音符(即不重复音符)个数跟变一个是一样的,所以我只考虑连续相同音符的最后一个的变化,一律让其+1。因为音符是非递减的顺序,明显差分可以表示出规律。连续的0的最后一个+1,其后一个正整数-1。从前往后一个一个变,最后判断差分中正数的个数即可。

例如:1 1 3 4 4 5的差分为1 0 2 1 0 1 -5(最后一个在这没啥用处)
第一次变化之后:1 1 1 1 0 1
第二次变化之后:1 1 1 1 1 0
第三次变化之后:1 1 1 1 1 1
最后结果为6

例如:1 1 1 2 2 2 的差分为1 0 0 1 0 0
第一次变化之后:1 0 1 0 0 0
第二次变化之后:1 0 1 0 0 1
最后结果为3

在这,差分可以简化为x,y,x是差分前一个数,y是差分中这个位置的数。
x经过判断并变化之后,如果大于0,则计数。
a数组最后一个数没有用过差分,但是不管它是不是与前一个相同的,都可以变或者不变来达到和前一个不一样。所以最后cnt++。
AC代码

#include <iostream>
using namespace std;

int main()
{
    int t,n;
    cin>>t;
    while(t--)
    {
        int a[100005]={},cnt=0;
        cin>>n>>a[0];
        int x=a[0],y=0;
        for(int i=1;i<n;i++)
        {
            cin>>a[i];
            y=a[i]-a[i-1];
            if(x==0&&y!=0)
            {
                x++;
                y--;
            }
            if(x>0) cnt++;
            x=y;
        }
        printf("%d\n",++cnt);
    }
    return 0;
}

B - Last Year’s Substring

Last Year’s Substring

思路

下面几种情况可以把其在一次操作以内变成“2020”:
①“ ⋯ \cdots 2020”
②“2 ⋯ \cdots 020”
③“20 ⋯ \cdots 20”
④“202 ⋯ \cdots 0”
⑤“2020 ⋯ \cdots
列举判断出来就可以

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

int main()
{
    int t,n;
    string s;
    cin>>t;
    while(t--)
    {
        cin>>n;
        cin>>s;
        if(s[0]=='2'&&s[n-3]=='0'&&s[n-2]=='2'&&s[n-1]=='0')
        {
            printf("YES\n");
        }
        else if(s[0]=='2'&&s[1]=='0'&&s[n-2]=='2'&&s[n-1]=='0')
        {
            printf("YES\n");
        }
        else if(s[0]=='2'&&s[1]=='0'&&s[2]=='2'&&s[n-1]=='0')
        {
            printf("YES\n");
        }
        else if(s[0]=='2'&&s[1]=='0'&&s[2]=='2'&&s[3]=='0')
        {
            printf("YES\n");
        }
        else if(s[n-4]=='2'&&s[n-3]=='0'&&s[n-2]=='2'&&s[n-1]=='0')
        {
            printf("YES\n");
        }
        else printf("NO\n");
    }
    return 0;
}

C - QAQ

QAQ

思路

每个‘A’都可能构成“QAQ”,某个’A‘左边的’Q‘的个数乘以右边的个数就是这个’A‘可以构成“QAQ”的个数,每个’A‘的个数累加即为结果。

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

int main()
{
    int cnt=0,cnt1=0,sum=0;
    int l[105]={};//第i个'A'左边的'Q'的个数
    string s;
    cin>>s;
    int len=s.size();
    for(int i=0;i<len;i++)
    {
        if(s[i]=='Q') cnt++;
        if(s[i]=='A')
        {
            l[cnt1++]=cnt;
        }
    }
    for(int i=0;i<cnt1;i++)
    {
        sum+=l[i]*(cnt-l[i]);
    }
    printf("%d\n",sum);
    return 0;
}

D - Maximum Increase

Maximum Increase

思路
cnt计数,一旦此次输入的数不大于前一个数,那么cnt清成1,否则cnt++,记录最大的cnt。

#include <iostream>
using namespace std;
int main()
{
    int n,m,p=0,cnt=0,maxc=0;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>m;
        if(m>p) cnt++;
        else cnt=1;
        if(cnt>maxc) maxc=cnt;
        p=m;//记录本次数字
    }
    cout<<maxc<<endl;
    return 0;
}

E - The Way to Home

The Way to Home

思路

想让次数最少只需要让每次的步长尽量最长。
在这里我在原字符串最后添上d-1个’1‘,判断的时候可以防止越界

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

int main()
{
    int n,d,cnt=0,i=0;
    string s;
    cin>>n>>d;
    cin>>s;
    for(int i=1;i<d;i++)
    {
        s=s+"1";
    }
    while(i<n-1)
    {
        int k=i;
        for(int j=d;j>0;j--)
        {
            if(s[i+j]=='1')
            {
                cnt++;
                i+=j;
                break;
            }
        }
        if(i==k)
        {
            printf("-1\n");
            break;
        }
    }
    if(i>=n-1) printf("%d\n",cnt);
    return 0;
}

F - Hit the Lottery

Hit the Lottery

思路
一笔钱可以表示成 1 × a + 5 × b + 10 × c + 20 × d + 100 × e = 1 × a + 5 × ( b + 2 × ( c + 2 × ( d + 5 × e ) ) ) 1×a+5×b+10×c+20×d+100×e=1×a+5×(b+2×(c+2×(d+5×e))) 1×a+5×b+10×c+20×d+100×e=1×a+5×(b+2×(c+2×(d+5×e)))

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

int main()
{
    int n,sum=0;
    cin>>n;
    sum+=n%5;
    n/=5;
    sum+=n%2;
    n/=2;
    sum+=n%2;
    n/=2;
    sum+=n%5;
    n/=5;
    sum+=n;
    cout<<sum<<endl;
    return 0;
}

G - Alex and a Rhombus

Alex and a Rhombus

#include <iostream>
using namespace std;
typedef long long ll;
int main()
{
    ll n,sum=1;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        sum+=4*(i-1);
    }
    cout<<sum<<endl;
    return 0;
}

H - Strange Birthday Party

Strange Birthday Party

思路

价格是从小到大排序的,所以 k i k_i ki越大, c k i c_{k_i} cki越大。
k i k_i ki大的尽量给它价格小的。

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

int k[300005],c[300005];
int main()
{
    int t,n,m;
    cin>>t;
    while(t--)
    {
        ll sum=0;
        cin>>n>>m;
        for(int i=0;i<n;i++) cin>>k[i];
        for(int i=1;i<=m;i++) cin>>c[i];
        sort(k,k+n);
        int i=0,j=1;
        //从大到小遍历
        for(i=n-1;k[i]>=j&&i>=0&&j<=m;i--,j++)
        {
            sum+=c[j];//给它价格小的
            //一旦j和k[i]接住了,说明价格小于c[j]的都送出去了,而剩下的人的礼物价格都不大于c[j]
        }
        //剩下的直接给钱
        for(;i>=0;i--)
        {
            sum+=c[k[i]];
        }
        cout<<sum<<endl;
    }
    return 0;
}

I - Piggy-Bank

Piggy-Bank

思路

完全背包,dp数组初始化为最大值,外层枚举物品,内层正序枚举容量

for(int i=1;i<=n;i++) cin>>a[i];//价值
for(int i=1;i<=n;i++) cin>>b[i];//体积

//经典0-1背包
for(int i=1;i<=n;i++)			//外层枚举物品
	for(int j=v;j>=b[i];j--)	//内层逆序枚举容量
		f[j]=max(f[j],f[j-b[i]]+a[i]);
		
//完全背包
for(int i=1;i<=n;i++)			//外层枚举物品
	for(int j=b[i];j<=v;j++)	//内层正序枚举容量
		f[j]=max(f[j],f[j-b[i]]+a[i]);
#include <iostream>
#define INF 0x7ffffff
using namespace std;

int main()
{
    int t,a,b,n;
    cin>>t;
    while(t--)
    {
        int p[505]={},w[505]={},f[10005]={};
        cin>>a>>b>>n;
        for(int i=1; i<=b-a; i++)
            f[i]=INF;
        for(int i=1; i<=n; i++)
        {
            cin>>p[i]>>w[i];
            for(int j=w[i]; j<=b-a; j++)
            {
                f[j]=min(f[j],f[j-w[i]]+p[i]);
            }
        }
        if(f[b-a]==INF)
            printf("This is impossible.\n");
        else
            printf("The minimum amount of money in the piggy-bank is %d.\n",f[b-a]);
    }
    return 0;
}

J - 饭卡

饭卡

思路

参考了网上的思路

输入卡中余额m,则扣掉最后的五块钱,用m-5来dp,然后加上最后买的那份价格最大的。如果直接拿m来dp,则可能出现选出来的要买的菜的价格都小于5,那么有几个菜按题意是不能要的。

#include <iostream>
#include <algorithm>
#define INF 0x7ffffff
using namespace std;

int main()
{
    int n,m;
    while(scanf("%d",&n)!=EOF&&n!=0)
    {
        int a[1005]={},f[1100]={};
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        sort(a,a+n);
        scanf("%d",&m);
        for(int i=0;i<n-1;i++)
        {
            for(int j=m-5;j>=a[i];j--)
            {
                f[j]=max(f[j-a[i]]+a[i],f[j]);
            }
        }
        if(m<5) printf("%d\n",m);
        else printf("%d\n",m-f[m-5]-a[n-1]);
    }
    return 0;
}

K - Bone Collector

Bone Collector

经典01背包

#include <iostream>
using namespace std;

int main()
{
    int t,n,v;
    cin>>t;
    while(t--)
    {
        int a[1005]={},b[1005]={},f[10005]={};
        cin>>n>>v;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++) cin>>b[i];
        for(int i=1;i<=n;i++)
        {
            for(int j=v;j>=b[i];j--)
            {
                f[j]=max(f[j],f[j-b[i]]+a[i]);
            }
        }
        printf("%d\n",f[v]);
    }
    return 0;
}

L - Anniversary party

Anniversary party

思路

每个结点两种状态:0不参加,1参加
则某结点 r ,0状态时,直接下属随意 ,1状态时直接下属状态为0
d p [ r ] [ 0 ] = ∑ m a x { d p [ i ] [ 0 ] , d p [ i ] [ 1 ] } d p [ r ] [ 1 ] = w [ r ] + ∑ d p [ i ] [ 0 ] i ∈ s o n [ r ] dp[r][0] = \sum max\{dp[i][0],dp[i][1]\}\\ dp[r][1] = w[r] +\sum dp[i][0]\\ i∈son[r] dp[r][0]=max{dp[i][0],dp[i][1]}dp[r][1]=w[r]+dp[i][0]ison[r]

#include <iostream>
using namespace std;

vector<int> son[6005];
int w[6005]={},dp[6005][2];
void dfs(int r)
{
    vector<int> t=son[r];
    int n=t.size();
    for(int i=0;i<n;i++)
    {
        dfs(t[i]);
        dp[r][0]+=max(dp[t[i]][0],dp[t[i]][1]);
        dp[r][1]+=dp[t[i]][0];
    }
    dp[r][1]+=w[r];
}

int main()
{
    int n,k,l;
    while(cin>>n)
    {
        memset(dp,0,sizeof(dp));
        memset(w,0,sizeof(w));
        int p[6005]={},root=1;
        for(int i=1;i<=n;i++) cin>>w[i];
        while(scanf("%d%d",&k,&l)!=EOF&&k!=0&&l!=0)
        {
            p[k]=l;
            son[l].push_back(k);
        }
        while(p[root]) root=p[root];
        
        dfs(root);

        cout<<max(dp[root][0],dp[root][1])<<endl;
        
        for(int i=0;i<=n;i++) son[i].clear();
    }
    return 0;
}

N - Halloween Costumes

Halloween Costumes

区间DP

for(int len=1; len<=n; len++)//枚举长度
{
	for(int i=1; i+len-1<=n; i++)//枚举起点j
	{
		int j=i+len-1;//另一个端点为i+len-1
		for(int k=i; k<j; k++)
		{
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+something);
		}
	}
}
#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    int t,n,cnt=0,a[105],dp[105][105];
    cin>>t;
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        cin>>n;
        for(int i=1; i<=n; i++)
        {
            cin>>a[i];
        }
        for(int len=1; len<=n; len++)
        {
            for(int i=1; i+len-1<=n; i++)
            {
                int ends=i+len-1;
                dp[i][ends]=dp[i][ends-1]+1;
                for(int j=i; j<ends; j++)
                {
                    if(a[j]==a[ends])
                    {
                        dp[i][ends]=min(dp[i][ends],dp[i][j]+dp[j+1][ends-1]);
                    }
                }
            }
        }
        printf("Case %d: %d\n",++cnt,dp[1][n]);
    }
    return 0;
}

P - Charlie’s Change

Charlie’s Change

思路

因为硬币可以重复取,所以用了完全背包 d p [ j ] dp[j] dp[j] 表示总价值为 j j j 的硬币的最多数量,外层枚举硬币种类 a [ i ] a[i] a[i],内层枚举价值求 d p [ j ] dp[j] dp[j] d p [ j − a [ i ] ] dp[j-a[i]] dp[ja[i]] 如果已经求出,而且 d p [ j − a [ i ] ] + 1 > d p [ j ] dp[j-a[i]]+1>dp[j] dp[ja[i]]+1>dp[j],即总价值为 j − a [ i ] j-a[i] ja[i] 的硬币加上一个 a[i] 硬币,那么 d p [ j ] dp[j] dp[j] 更新为 d p [ j − a [ i ] ] + 1 dp[j-a[i]]+1 dp[ja[i]]+1

但由于每种硬币的数量有限,所以要有限制条件,硬币 a [ i ] a[i] a[i] 没取完的时候才可以取 a [ i ] a[i] a[i],怎么算是没取完?我用了二维数组, b [ j ] [ i ] b[j][i] b[j][i] 来表示总价值为 j j j 的硬币取法: b [ j ] [ 1 ] b[j][1] b[j][1] b [ j ] [ 2 ] b[j][2] b[j][2] b [ j ] [ 3 ] b[j][3] b[j][3] b [ j ] [ 4 ] b[j][4] b[j][4]分别表示硬币 a [ 1 ] a[1] a[1] a [ 2 ] a[2] a[2] a [ 3 ] a[3] a[3] a [ 4 ] a[4] a[4]的数量,每次刷新的时候要把 b [ j − a [ i ] ] [ k ] b[j-a[i]][k] b[ja[i]][k] 转存到 b [ j ] [ k ] b[j][k] b[j][k] 里,其中只有 b [ j ] [ i ] = b [ j − a [ i ] ] [ i ] + 1 b[j][i]=b[j-a[i]][i]+1 b[j][i]=b[ja[i]][i]+1,因为总价值为 j j j 和总价值为 j − a [ i ] j-a[i] ja[i] 硬币取法只有硬币 a [ i ] a[i] a[i] 不一样,要加一。这样判断没取完的条件就是:

	b[j-a[i]][i]<c[i]

该开始没有把dp初始化成这样:

    memset(dp,-1,sizeof(dp));
    dp[0]=0;

判断的条件里也没有:

	dp[j-a[i]]!=-1

所以WA了,在网上找到一篇和我的思路一样的,发现人家这两个比我多了这几句,想了想,确实是自己疏忽了,因为如果像我那样,只把dp数组初始化为全零,也没有加那条判断语句的话,在 j − a [ i ] j-a[i] ja[i] 并没有求出来(即dp值为0)的情况下,总价值 j − a [ i ] j-a[i] ja[i] 的硬币没法儿取,那总价值为 j j j 的也没法取,只加一个 a [ i ] a[i] a[i] 硬币不符合要求。加上这几句之后对了。

#include <iostream>
#include <string>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#define INF 0x7ffffff
using namespace std;

typedef long long ll;

int main()
{
    int p,c[5]={},a[5]={0,1,5,10,25},b[10005][5]={};
    while(scanf("%d%d%d%d%d",&p,&c[1],&c[2],&c[3],&c[4])!=EOF)
    {
        if(!p&&!c[1]&&!c[2]&&!c[3]&&!c[4]) break;
        int dp[10005]={};
        memset(b,0,sizeof(b));
        memset(dp,-1,sizeof(dp));
        dp[0]=0;
        for(int i=1;i<=4;i++)
        {
            for(int j=a[i];j<=p;j++)
            {
                if(dp[j-a[i]]!=-1&&dp[j-a[i]]+1>dp[j]&&b[j-a[i]][i]<c[i])
                {
                    dp[j]=dp[j-a[i]]+1;
                    b[j][i]=b[j-a[i]][i]+1;
                    for(int k=1;k<i;k++)
                        b[j][k]=b[j-a[i]][k];

                }
            }
        }
        if(dp[p]!=-1)
            printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",b[p][1],b[p][2],b[p][3],b[p][4]);
        else
            printf("Charlie cannot buy coffee.\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值