陈太阳与乐谱
思路
首先将乐谱抽象为一个序列(note_id,Vol),表示以Vol的音量按了钢琴上编号为note_id的琴键。注意这个note_id应该用唯一的编号。
将问题分为两部分:音量代价与音符代价。这两部分的代价计算是分开的。
音量代价:只有在按音符时音量与当前音量不同时计算代价。注意默认音量是100。
音符代价:可以使用动态规划计算。dp[x][y]表示按完前x个音符,当前音阶为y的最小代价。直接按照题目所给信息转移即可。也可以使用最短路。
时间复杂度:O(n88)
代码
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9+7;
int n,tot,ans,vol,o,dp[100005][10];
pair<int, int> a[100005];
char s[100005];
void init()
{
tot=ans=0;
o=4,vol=100;
for(int i=0;i<=n+3;i++)
for(int j=0;j<=8;j++)
dp[i][j]=inf;
dp[0][4]=0;
}
bool note(int i)
{
return s[i]>='A'&&s[i]<='G';
}
int trans(int x,int y)
{
return min(abs(x-y),2);
}
int get(int i)
{
if(s[i]=='C'&&s[i+1]=='-')
return 0;
if(s[i]=='C'&&s[i+1]=='+'||s[i]=='D'&&s[i+1]=='-')
return 2;
if(s[i]=='D'&&s[i+1]=='+'||s[i]=='E'&&s[i+1]=='-')
return 4;
if(s[i]=='E'&&s[i+1]=='+')
return 6;
if(s[i]=='F'&&s[i+1]=='-')
return 5;
if(s[i]=='F'&&s[i+1]=='+'||s[i]=='G'&&s[i+1]=='-')
return 7;
if(s[i]=='G'&&s[i+1]=='+'||s[i]=='A'&&s[i+1]=='-')
return 9;
if(s[i]=='A'&&s[i+1]=='+'||s[i]=='B'&&s[i+1]=='-')
return 11;
if(s[i]=='B'&&s[i+1]=='+')
return 13;
if(s[i]=='C')
return 1;
if(s[i]=='D')
return 3;
if(s[i]=='E')
return 5;
if(s[i]=='F')
return 6;
if(s[i]=='G')
return 8;
if(s[i]=='A')
return 10;
if(s[i]=='B')
return 12;
}
int main()
{
while(1)
{
scanf("%s",s+1);
if(s[1]=='*')
break;
n=strlen(s+1);
init();
int cnt=0,last=100;
for(int i=1;i<=n;i++)
{
if(note(i)&&last!=vol)
{
cnt+=tot;
last=vol;
tot=0;
}
if(s[i]=='V')
{
int len=0,val=0;
for(int j=i+1;s[j]>='0'&&s[j]<='9';j++)
val=val*10+s[j]-'0',len++;
if(val!=vol)
vol=val,tot=len+1;
}
}
tot=0;
for(int i=1;i<=n;i++)
{
if(note(i))
{
int id=get(i);
a[++tot]=make_pair(o,id);
}
if(s[i]=='O')
{
int val=0;
for(int j=i+1;s[j]>='0'&&s[j]<='9';j++)
val=val*10+s[j]-'0';
o=val;
}
if(s[i]=='>')
o++;
if(o>8)
o=8;
if(s[i]=='<')
o--;
if(o<1)
o=1;
}
for(int i=1;i<=tot;i++)
{
int oc=a[i].first,id=a[i].second;
if(id==2||id==4||id==7||id==9||id==11)
{
for(int j=1;j<=8;j++)
dp[i][oc]=min(dp[i][oc],dp[i-1][j]+trans(oc,j)+2);
}
if(id==3||id==5||id==6||id==8||id==10)
{
for(int j=1;j<=8;j++)
dp[i][oc]=min(dp[i][oc],dp[i-1][j]+trans(oc,j)+1);
}
if(id==0)
{
for(int j=1;j<=8;j++)
dp[i][oc]=min(dp[i][oc],dp[i-1][j]+trans(oc,j)+2);
for(int j=1;j<=8;j++)
dp[i][oc-1]=min(dp[i][oc-1],dp[i-1][j]+trans(oc-1,j)+1);
}
if(id==1)
{
for(int j=1;j<=8;j++)
dp[i][oc]=min(dp[i][oc],dp[i-1][j]+trans(oc,j)+1);
for(int j=1;j<=8;j++)
dp[i][oc-1]=min(dp[i][oc-1],dp[i-1][j]+trans(oc-1,j)+2);
}
if(id==12)
{
for(int j=1;j<=8;j++)
dp[i][oc]=min(dp[i][oc],dp[i-1][j]+trans(oc,j)+1);
for(int j=1;j<=8;j++)
dp[i][oc+1]=min(dp[i][oc+1],dp[i-1][j]+trans(oc+1,j)+2);
}
if(id==13)
{
for(int j=1;j<=8;j++)
dp[i][oc]=min(dp[i][oc],dp[i-1][j]+trans(oc,j)+2);
for(int j=1;j<=8;j++)
dp[i][oc+1]=min(dp[i][oc+1],dp[i-1][j]+trans(oc+1,j)+1);
}
}
ans=inf;
for(int i=1;i<=8;i++)
ans=min(ans,dp[tot][i]);
printf("%d\n",ans+cnt);
}
return 0;
}
来源:zr