week2
A - 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
思路
下面几种情况可以把其在一次操作以内变成“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
思路
每个‘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
思路
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
思路
想让次数最少只需要让每次的步长尽量最长。
在这里我在原字符串最后添上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
思路
一笔钱可以表示成
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
#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
思路
价格是从小到大排序的,所以
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
思路
完全背包,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
经典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
思路
每个结点两种状态: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]i∈son[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
区间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
思路
因为硬币可以重复取,所以用了完全背包, 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[j−a[i]] 如果已经求出,而且 d p [ j − a [ i ] ] + 1 > d p [ j ] dp[j-a[i]]+1>dp[j] dp[j−a[i]]+1>dp[j],即总价值为 j − a [ i ] j-a[i] j−a[i] 的硬币加上一个 a[i] 硬币,那么 d p [ j ] dp[j] dp[j] 更新为 d p [ j − a [ i ] ] + 1 dp[j-a[i]]+1 dp[j−a[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[j−a[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[j−a[i]][i]+1,因为总价值为 j j j 和总价值为 j − a [ i ] j-a[i] j−a[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] j−a[i] 并没有求出来(即dp值为0)的情况下,总价值 j − a [ i ] j-a[i] j−a[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;
}