您的位置:首页 > 编程语言 > C语言/C++

Codeforces Round #283(Div.2) A,B,C,D,E 解题报告

2015-08-15 16:08 267 查看
转载请注明出处,谢谢

http://blog.csdn.net/wyBluewind/article/details/47682783

codeforces 上写的题解 by Endagorion:http://codeforces.com/blog/entry/15208

写在前面:第一次写博客,也是第一次写题解,写的不好请各路高手指导。

A. Minimum Difficulty

题意:

简单题。

思路:

找一下挨着的差的最大值,然后间隔为2时最大的最小即可。详细见代码。

代码:

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

int n,d1,d2,ans;
int a[1009];

int main()
{
while(cin >> n){
d1=d2=-1;
for(int i=0; i<n; ++i)
cin >> a[i];
for(int i=1; i<n; ++i)
d1=max(a[i]-a[i-1],d1);
ans=100000;
for(int i=2; i<n; ++i){
d2 = max(a[i]-a[i-2],d1);
ans=min(ans,d2);
}
cout << ans << endl;
}
return 0;
}


B. Secret Combination

题目大意:

有一个n位的数字组合锁,锁上有两个按钮可以改变显示的数字。第一个按钮是将n位数字每一位都加一(9 会变成0),第二个按钮将所有位右移一位(最后一位变成第一位)。按下第一个按钮:579 会变成 680 , 再按下第二个按钮 680 会变成 068。

需要找到可以显示出的最小的数,前导零会被忽略。

思路:

肯定是前导0越多数越小。所以第一位一定是0,然后找出每一位在第一位且变成0时的显示的数字大小,然后比较即可。

代码:

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

int n,len;
string s,ans,temp;

int main(){
//    ifstream cin;
//    cin.open("in.txt");

while(cin >> n){
cin >> s;
len =s.size();
temp = s + s; ans=s;
for(int i=0; i<len; ++i){
int t = 10-(temp[i]-'0');
s="";
for(int j=i; j<i+len; ++j){
s += (char)(((temp[j]-'0')+t)%10 + '0');
}
if(ans>s)
ans = s;
}
cout << ans << endl;
}
return 0;
}


C. Removing Columns

题目大意:

一个由小写英文字母组成的n×m的表格,你可以删除表格中的某一列。如果一个表格从上到下满足按字典序递增(可以相同),则它就是一个
‘good table’
,问最少删除几列可以变成
‘good table’


思路:

从简单的入手就一行行的比较,如果不满足要求就把这列标记为删除,注意这列删除了可能会影响前面比较完的结果,比如

aab

aba

aaa

第三行和第二行比较时,删除第二列之后会影响到第二行和第一行的比较结果。所以每次删除一列之后,都要从第一行重新比较。此时我们需要记录一下每一行和前一行在哪一列比较出的结果,每次从这个标记列开始比较就好了,不用每次都从第一列开始比较。最后统计一下删除的列就行了。

这个时间复杂度,因为最多有m次重新比较,每次比较的代价就是n×m的, 所以就是O(m×n×m)。[我也不知道对不对,有错误的话请告诉我,我改正,谢谢了]

代码:

#include<iostream>
#include<fstream>
#include<cstring>
using namespace std;

int n,m;
bool is[105];
int flg[109];
char grid[105][105];

int main()
{
//    ifstream cin;
//    cin.open("in.txt");

while(cin >> n >> m){
for(int i=0; i<n; ++i){
for(int j=0; j<m; ++j){
cin >> grid[i][j];
}
}
memset(is,true,sizeof(is));
memset(flg,0,sizeof(flg));

for(int i=1; i<n; ++i){
for(int j=flg[i]; j<m; ++j){
if(is[j]){
if(grid[i][j] < grid[i-1][j]){
is[j]=false;
i=0;
break;
}
else if(grid[i][j] == grid[i-1][j]){
flg[i]=j;
}
else{
flg[i]=j;
break;
}
}
}
}

int ans=0;
for(int i=0; i<m; ++i)
if(!is[i])
ans++;
cout << ans << endl;

}
return 0;
}


D. Tennis Game

题目大意:

Petya和Gena打乒乓球,一场比赛有很多局,每局有多次发球。每赢一次发球得1分,每局先赢t分的人赢了这局,下局重新记分,先赢s局的人赢得比赛。现在有一系列记录每次发球的胜者,但是不知道t和s,需要找出满足这个记录的所有t和s。

思路:

对于这种找满足要求的数的,我觉得就是枚举。直接枚举一定会超时,O(n^2)需要优化。

这题先枚举t,然后找满足的s,当然对于固定的t最多只有一个s与之对应。当一局结束后,向后找t个1和t个2,哪个先出现这局就是他赢了。可以利用二分查找来找t个1和t个2,这样就可以优化到O(nlogn)了(我之前用二分搜索实现的但是TLE了 >_< ,后来用了一个机智的方法用O(1)的时间代替了二分查找)。

注意几个问题:若1和2的个数都不满足t个,那么就说明不满足要求,还有赢最后一局的人一定是最后的赢家(之前在这wa了)。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<fstream>
#include<utility>
#include<algorithm>
#include<vector>
using namespace std;

const int MAXN = 1000000;

int n;
int a[100009][2], pos[100009][2];
struct P{
int s;
int t;
}res[100009];
int num=0;
bool cmp(const P &a1, const P &a2){
if(a1.s!=a2.s)
return a1.s<a2.s;
else return a1.t<a2.t;
}

int main()
{
//    freopen("in.txt","r",stdin);

while(scanf("%d",&n)!=EOF){
memset(a,0,sizeof(a));
for(int i=1,t; i<=n; ++i){
scanf("%d", &t);
4000

if(t==1){
a[i][0]=a[i-1][0]+1;
a[i][1]=a[i-1][1];
pos[a[i][0]][0]=i;
}
else{
a[i][1]=a[i-1][1]+1;
a[i][0]=a[i-1][0];
pos[a[i][1]][1]=i;
}
}

int a1=0,a2=0;
num=0;
for(int i=1; i<=n; ++i){
int j=0; a1=a2=0; int last=-1;
while(j<n){
if(a[j][0]+i <= a
[0] && a[j][1]+i <= a
[1]){
int k1 = pos[a[j][0]+i][0];
int k2 = pos[a[j][1]+i][1];
if(k1<k2)
a1++,j=k1,last=1;
else if(k2<k1)
a2++,j=k2,last=2;
else break;
}
else if(a[j][0]+i <= a
[0] && a[j][1]+i > a
[1]){
a1++,j=pos[a[j][0]+i][0];last=1;
}
else if(a[j][0]+i > a
[0] && a[j][1]+i <= a
[1]){
a2++,j=pos[a[j][1]+i][1]; last=2;
}
else
break;
}
if(j==n && a1!=a2){
if(a1>a2 && last==1){
res[num].s=a1,res[num++].t=i;
}
if(a2>a1 && last==2){
res[num].s=a2, res[num++].t=i;
}
}
}
sort(res,res+num,cmp);
int len = num;
printf("%d\n",len);
for(int i=0; i<len; ++i){
printf("%d %d\n",res[i].s,res[i].t);
}
}
return 0;
}


E. Distributing Parts

题目大意:

一个音乐播放器有m个单元,每个单元可以播放一首音乐,每个单元一个音量范围c,d,只可以播放该音量范围内的音乐,且最多可以放k首音乐,也可以不放任何音乐。现在有n首音乐,每首音乐也有个音量范围a,b,需要放到m个单元中。输出每首音乐用哪个单元播放。

思路:

这种一个范围需要放到另一个范围的题目,就是放到一起排序,然后处理。

首先按照最小音量升序排序,如果相同那么单元放在前面。然后从前到后一个个处理,如果碰到单元就放到集合里,如果碰到音乐就在现在的集合中找一个最小的d,此时c一定大于a,就是我们要找的播放单元,同时k需要减1。如果单元的k为0,就从集合中删除。这个集合可以用c++中的set维护(需要了解一下set的实现方式和用到的数据结构)。复杂度O((n+m)log(n+m));

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;

struct Node{
int id,beg,en,flg,k;
Node(){beg=0;en=0;flg=0;k=0;}
inline bool operator < (const Node &a) const {
if(beg != a.beg)
return beg<a.beg;
else return flg>a.flg;
}
}all[100009*2];

bool cmp1(const Node &a, const Node &b){
if(a.beg != b.beg)
return a.beg<b.beg;
else return a.flg>b.flg;
}

struct cmp2{
bool operator ()(const Node &a, const Node &b){
return a.en<b.en;
}
};

multiset<Node, cmp2> sets;
multiset<Node, cmp2>::iterator it;

int n,m;
int ans[100009];
int used[100009];

int main()
{
//    freopen("in.txt","r",stdin);
while(cin >> n ){
for(int i=1,s,e; i<=n; ++i){
Node temp;
cin >> s >> e;
temp.beg = s;
temp.en = e;
temp.flg = 0;
temp.id = i;
all[i] = temp;
}
cin >> m;
for(int i=1,s,e,k; i<=m; ++i){
Node temp;
cin >> s >> e >> k;
temp.beg = s;
temp.en = e;
temp.flg = 1;
temp.k = k;
temp.id = i;
all[i+n] = temp;
}
sort(all+1, all+1+n+m, cmp1);

bool flgs = true;
memset(used,0,sizeof(used));
memset(ans,0,sizeof(ans));
for(int i=1; i<=n+m; ++i){
if(all[i].flg==1){
sets.insert(all[i]);
}
else{
it = sets.lower_bound(all[i]);
if(it != sets.end()){
ans[all[i].id] = it->id;
used[it->id]++;
if(it->k == used[it->id] ){
sets.erase(it);
}
}
else{
flgs=false;
break;
}
}
}
if(flgs){
printf("YES\n%d", ans[1]);
for(int i=2; i<=n; ++i)
printf(" %d",ans[i]);
printf("\n");
}
else printf("NO\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息