题解
今天这把题有点简单,除了第二题数学方法没想到之外,剩下的250草草收场。
第一题——音量调节(changingsounds)
【题目描述】
- 一个**DJ要玩调音,给了beginlevel和maxlevel,又给了若干个音量调整,从beginlevel开始,从1-n可加可减,不能超过0-maxlevel的限制,问你到第n首调完最大音量是多少。
- 我一看卧槽这不是妥妥的广搜题么!然后我抄起键盘,复制粘贴就是干!五分钟打完广搜。
- 然后我一看数据, n≤50 n ≤ 50 ,那广搜怕是要爆,果断改变策略。
想了想,可以当做dp题来做。设个布尔数组,从1-n之间枚举,判断是否为合法情况。
方程如下
if(j+c[i]≤maxlevel i f ( j + c [ i ] ≤ m a x l e v e l )dp[i][j]=dp[i][j]||dp[i−1][j+c[i]]; ) d p [ i ] [ j ] = d p [ i ] [ j ] | | d p [ i − 1 ] [ j + c [ i ] ] ;
if(j−c[i]≤0 i f ( j − c [ i ] ≤ 0 ) ) 然后扫一遍最后的n就可以得到最大值。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;
void fff(){
freopen("changingsounds.in","r",stdin);
freopen("changingsounds.out","w",stdout);
}
const int MAXN=55;
int begin_level,max_level,n;
int c[MAXN];
bool dp[MAXN][1010];
int ans=0;
int main(){
fff();
scanf("%d%d%d",&n,&begin_level,&max_level);
for (int i=1;i<=n;i++){
scanf("%d",&c[i]);
}
dp[0][begin_level]=true;
for (int i=1;i<=n;i++){
for (int j=max_level;j>=0;j--){
if(j+c[i]<=max_level){
dp[i][j]=dp[i][j]||dp[i-1][j+c[i]];
}
if(j-c[i]>=0){
dp[i][j]=dp[i][j]||dp[i-1][j-c[i]];
}
}
}
for (int i=max_level;i>=0;i--){
if(dp[n][i]){
cout<<i;
return 0;
}
}
cout<<-1;
return 0;
}
第二题——旅行(journey)
【题目描述】
- 就是给你一张图,X是挡住路径的但不会封死路径,. 是可通过路径,数据给定保证每行每列最多有1个X,而50%的数据是不含有X的。让你求随机起点到随机终点的平均路径。
- 公式:(假设有p个点)
∑pi,j=1|xi−xj|+∣∣yi−yj∣∣p2 ∑ i , j = 1 p | x i − x j | + | y i − y j | p 2
- 你会发现其实你可以骗50分,而且数据也不大,只要枚举每一行,从这一行开始往后扫一遍就可以了。根据可逆性节省一半时间orz,五十分轻松到手。
- 对于剩下的50分,你会发现,如果是按照公式就是直接走,但会有X挡路,所有需要越过X,而每越过一个X,你的距离就会增加2。由于没有连续的对角线挡路,所以只要考虑多少对 <i,j> < i , j > <script type="math/tex" id="MathJax-Element-16"> </script>被X挡住了。
- 但是很不幸,我刚开始只想到了在同一行、列内如果挡住了会有需要绕过X的情况,但其实如果不同的两行、列之间,所走的曼哈顿路径也被X挡住了,那也是需要进行绕路的,这个没想到ojz
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#define LL long long
using namespace std;
void fff(){
freopen("journey.in","r",stdin);
freopen("journey.out","w",stdout);
}
const int MAXN=1010;
int a[MAXN][MAXN];
double ans=0,l[MAXN],h[MAXN],h1[MAXN],l1[MAXN],tot,n,m;
int main(){
fff();
scanf("%lf%lf\n",&n,&m);
for (LL i=1;i<=n;i++){
h[i]=214748347;
}
for (LL i=1;i<=m;i++){
l[i]=214748347;
}
for (LL i=1;i<=n;i++){
for (LL j=1;j<=m;j++){
char c;
scanf("%c",&c);
if(c=='X'){
h[i]=j;
l[j]=i;
a[i][j]=1;
}else{
a[i][j]=1;
tot++;
h1[i]++;
l1[j]++;
}
}
if(i!=n) scanf("\n");
}
for (LL i=1;i<=n;i++){
for (LL j=i;j<=n;j++){
ans+=(h1[i]*h1[j]*(j-i)*2.0)/tot/tot;
}
}
for (LL i=1;i<=m;i++){
for (LL j=i;j<=m;j++){
ans+=(l1[i]*l1[j]*(j-i)*2.0)/tot/tot;//记录行、 列之间.的乘机和,有对称可逆性可以减少复杂度
}
}
for (LL i=1;i<=m;i++){
if(l[i]<214748347){
double jy=l[i]-1;
for (int j=i;j>1&&l[j-1]<l[j];j--) jy+=l[j-1]-1;//就是那个被挡住曼哈顿路径的也需要进行增加,而增加的点数就是l[j-1]到l[j]之间的点数了,无限被挡orz
for (int j=i;j<m&&l[j+1]<l[j];j++) jy+=l[j+1]-1;
ans+=4.0*(n-l[i])*jy/tot/tot;//对称可逆,越过一个x要加2,那么对称就要加4
}
}
for (LL i=1;i<=n;i++){
if(h[i]<214748347){
double jy=h[i]-1;
for (int j=i;j>1&&h[j-1]<h[j];j--) jy+=h[j-1]-1;
for (int j=i;j<n&&h[j+1]<h[j];j++) jy+=h[j+1]-1;
ans+=4.0*(m-h[i])*jy/tot/tot;//最后再来算一把就ok了
}
}
printf("%.4lf\n",ans);
return 0;
}
第三题——舞蹈课(dancinglessons)
【题目描述】
让你模拟跳舞的场景。在队列当中是异性的组合,默契差最小的出队,而剩下的人自动补上位置,知道不能再出队列。
- 一看到默契差最小,我就知道要用堆来维护这个最小值。剩下的工作就是预处理和维护合法情况。十五分钟就打完了。一遍就AC。手感是真的棒。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
using namespace std;
void fff(){
freopen("dancinglessons.in","r",stdin);
freopen("dancinglessons.out","w",stdout);
}
int n;
const int MAXN=200010;
struct node{
bool is_gay,is_out;
int l,r;
int val;
node(){
is_out=false;
}
}a[MAXN];
int _abs(int a){
if(a<0) a=-a;
return a;
}
struct min_pair{
int delta;
int l,r;
bool operator <(const min_pair x) const{
if(delta==x.delta) return l>x.l;
return delta>x.delta;
}
};
int k=0;
struct Ans{
int l,r;
}ans[MAXN];
priority_queue <min_pair> heap;
char s[MAXN];
int main(){
fff();
scanf("%d",&n);
cin>>s;
a[1].l=0;
a[n].r=n+1;
for (int i=1;i<=n;i++){
scanf("%d",&a[i].val);
if(s[i-1]=='B') a[i].is_gay=true;//看这个人是不是男的
else a[i].is_gay=false;
if(i!=1) a[i].l=i-1;
if(i!=n) a[i].r=i+1;
if(a[i].is_gay!=a[i-1].is_gay&&i!=1){
heap.push((min_pair){_abs(a[i].val-a[i-1].val),i-1,i});//先把相邻的合法的进堆
}
}
while (!heap.empty()){//堆维护最小值
min_pair e=heap.top(); heap.pop();
#define LF a[e.l].l
#define RF a[e.r].r
if(!a[e.l].is_out&&!a[e.r].is_out&&e.l!=0&&e.r<=n){//防止出现一个人已经出去了另一个人还在堆里
ans[++k]=(Ans){e.l,e.r};
a[e.l].is_out=true;
a[e.r].is_out=true;
a[LF].r=a[e.r].r;
a[RF].l=a[e.l].l;//指针迁移
if(a[LF].is_gay!=a[RF].is_gay){
heap.push((min_pair){_abs(a[LF].val-a[RF].val),LF,RF});//继续进堆
}
}
}
cout<<k<<'\n';
for (int i=1;i<=k;i++){
printf("%d %d\n",ans[i].l,ans[i].r);
}
return 0;
}