您的位置:首页 > 其它

第四次练习赛解题报告及标程

2014-04-19 01:37 225 查看
  第四次练习赛本来想有点创新,结果还是很不幸地做得不够好。

  A. AString.h(I)

  判断并输出所有是回文串的字符串,唯一要注意的仅仅是分割字符串。数据量其实很小。

//by trashLHC

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<fstream>
#define MAX 101
using namespace std;

bool isSymmetric(string s){
cout<<s<<endl;
for(int i=0;i<s.length()/2;i++)
if(s[i]!=s[s.length()-i-1]&&s[i]!=s[s.length()-i-1]-32&&s[i]!=s[s.length()-i-1]+32)
return false;
return true;
}

string solve(string s){
int place[MAX],top=0;
place[top++]=-1;
for(int i=0;i<s.length();i++)
if(s[i]==' ')
place[top++]=i;
place[top++]=s.length();

for(int i=top-1;i>0;i--){
if(!isSymmetric(s.substr(place[i-1]+1,place[i]-place[i-1]-1))){
s.erase(place[i-1]+1,place[i]-place[i-1]);
}
}
return s;
}

int main(){
//ifstream infile("in.txt",ios::in);
//ofstream outfile("out.txt",ios::out);
int n;
infile>>n;
infile.get();
while(n--){
string s;
getline(infile,s);
outfile<<solve(s)<<endl;
}
}
//by wjfwzzc

#include<cstdio>
#include<cstring>
#include<cctype>
using namespace std;
char str[105];
bool isPalin(char *s)
{
int l=strlen(s);
for(int i=0; i<(l>>1); ++i)
if(tolower(s[i])!=tolower(s[l-i-1]))
return false;
return true;
}
int main()
{
int n;
scanf("%d",&n);
getchar();
while(n--)
{
gets(str);
char *sub=strtok(str," ");
while(sub)
{
if(isPalin(sub))
printf("%s ",sub);
sub=strtok(NULL," ");
}
putchar('\n');
}
}

  B. AString.h(II)

  这道题更简单了,匹配字符串并删除,数据量同样很小。用string类自带函数就可以轻易解决。

#include<iostream>
#include<string>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
string s,t;
while(cin>>s)
{
cin.get();
getline(cin,t);
int k=t.find(s);
for(int i=t.find(s); i!=t.npos; i=t.find(s))
t.erase(i,s.size());
cout<<t<<endl;
}
}

  C. MH370的黑匣子

  就算它题干变得再花,也不过就是个迷宫。只判断是否存在路径,不要求路径最短,所以dfs即可;bfs也可以不过稍微难写一点点。

  以下分别放出两个版本,先是dfs的。

#include<cstdio>
#include<cstring>
using namespace std;
const int dx[]= {-1,0,1,0},dy[]= {0,1,0,-1};
char g[15][15];
bool dfs(int x,int y)
{
g[x][y]='#';
for(int i=0; i<4; ++i)
{
int tx=x+dx[i],ty=y+dy[i];
if(g[tx][ty]=='B'||(g[tx][ty]=='*'&&dfs(tx,ty)))
return true;
}
return false;
}
int main()
{
int n,m,sx,sy;
while(~scanf("%d%d",&n,&m))
{
memset(g,'#',sizeof(g));
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
{
scanf(" %c",&g[i][j]);
if(g[i][j]=='S')
{
sx=i;
sy=j;
}
}
puts(dfs(sx,sy)?"Yes":"No");
}
}

  然后是bfs的。

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int dx[]= {-1,0,1,0},dy[]= {0,1,0,-1};
char g[15][15];
int sx,sy;
bool bfs()
{
queue<pair<int,int> > q;
q.push(make_pair(sx,sy));
while(!q.empty())
{
pair<int,int> tmp=q.front();
q.pop();
for(int i=0; i<4; ++i)
{
int tx=tmp.first+dx[i],ty=tmp.second+dy[i];
if(g[tx][ty]=='B')
return true;
else if(g[tx][ty]=='*')
{
q.push(make_pair(tx,ty));
g[tx][ty]='#';
}
}
}
return false;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
memset(g,'#',sizeof(g));
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
{
scanf(" %c",&g[i][j]);
if(g[i][j]=='S')
{
sx=i;
sy=j;
}
}
puts(bfs()?"Yes":"No");
}
}

  D. 传话游戏

  那些个吐槽题干描述不清晰的童鞋们,这道题可是微软2013编程之美资格赛第1题原题……照例注意字符串的分割,然后模拟一下替换过程就好了。标程用了map来记录替换字典,也可以写个pair或者struct。

#include<cstdio>
#include<cstring>
#include<vector>
#include<string>
#include<map>
using namespace std;
int main()
{
int t,n,m;
char a[22],b[22],str[105];
scanf("%d",&t);
for(int cas=1; cas<=t; ++cas)
{
scanf("%d%d",&n,&m);
map<string,string> dic;
vector<string> stc;
vector<string>::iterator it;
while(m--)
{
scanf("%s%s",a,b);
dic[a]=b;
}
getchar();
gets(str);
char *sub=strtok(str," ");
while(sub)
{
stc.push_back(sub);
sub=strtok(NULL," ");
}
while(--n)
for(it=stc.begin(); it!=stc.end(); ++it)
if(dic[*it]!="")
*it=dic[*it];
printf("Case #%d:\n",cas);
for(it=stc.begin(); it!=stc.end(); ++it)
printf("%s ",(*it).c_str());
putchar('\n');
}
}

  E. Monotonic Stack

  接下来的三道题都属于开阔视野的类型。建议想要尝试ACM竞赛的童鞋好好研究一下。

  这道题模拟了单调栈的基本操作,同时在hint里给出了单调栈的入门练习题(POJ2559)。因为介绍它的文章和题解一大堆,这里不多解释。

#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int MAXN=100005;
int MonotonicStack[MAXN],top;
int main()
{
int m,e;
char op[10];
while(~scanf("%d",&m))
{
top=-1;
while(m--)
{
scanf("%s",op);
switch(op[1])
{
case 'U':
scanf("%d",&e);
while(top!=-1&&MonotonicStack[top]>=e)
--top;
MonotonicStack[++top]=e;
break;
case 'O':
if(top!=-1)
--top;
break;
case 'E':
if(top==-1)
puts("EMPTY");
else
printf("%d\n",MonotonicStack[top]);
break;
}
}
}
}
#include<cstdio>
#include<stack>
using namespace std;
stack<int> MonotonicStack;
int main()
{
int m,e;
char op[10];
while(~scanf("%d",&m))
{
while(m--)
{
scanf("%s",op);
switch(op[1])
{
case 'U':
scanf("%d",&e);
while(!MonotonicStack.empty()&&MonotonicStack.top()>=e)
MonotonicStack.pop();
MonotonicStack.push(e);
break;
case 'O':
if(!MonotonicStack.empty())
MonotonicStack.pop();
break;
case 'E':
if(MonotonicStack.empty())
puts("EMPTY");
else
printf("%d\n",MonotonicStack.top());
break;
}
}
while(!MonotonicStack.empty())
MonotonicStack.pop();
}
}

  F. Monotonic Queue

  这道题模拟了单调队列的基本操作,也给出了单调队列的入门练习题(POJ2823),和上面一样两道题都是很有趣的问题,而且相关题解众多,不多阐述。

#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int MAXN=1005;
int MonotonicQueue[MAXN],front,size;
int main()
{
int n,m,e;
char op[10];
while(~scanf("%d%d",&n,&m))
{
front=size=0;
while(m--)
{
scanf("%s",op);
switch(op[1])
{
case 'U':
scanf("%d",&e);
while(size>0&&MonotonicQueue[(front+size-1)%n]>=e)
--size;
if(size==n)
{
front=(front+1)%n;
--size;
}
MonotonicQueue[(front+(size++))%n]=e;
break;
case 'O':
if(size>0)
{
front=(front+1)%n;
--size;
}
break;
case 'E':
if(size==0)
puts("EMPTY");
else
printf("%d\n",MonotonicQueue[front]);
break;
}
}
}
}
#include<cstdio>
#include<deque>
using namespace std;
deque<int> MonotonicQueue;
int main()
{
int n,m,e;
char op[10];
while(~scanf("%d%d",&n,&m))
{
while(m--)
{
scanf("%s",op);
switch(op[1])
{
case 'U':
scanf("%d",&e);
while(!MonotonicQueue.empty()&&MonotonicQueue.back()>=e)
MonotonicQueue.pop_back();
if(MonotonicQueue.size()==n)
MonotonicQueue.pop_front();
MonotonicQueue.push_back(e);
break;
case 'O':
if(!MonotonicQueue.empty())
MonotonicQueue.pop_front();
break;
case 'E':
if(MonotonicQueue.empty())
puts("EMPTY");
else
printf("%d\n",MonotonicQueue.front());
break;
}
}
MonotonicQueue.clear();
}
}

  G. Jeffrey的组合数

  这道题就是最普通的组合数求模。我非常欣喜地看到童鞋们给出了相当多种不同的做法;虽然因为数据组数比较小,以至于有些以为会TLE的代码也过了,但依然为其中的想法感到很高兴。限于时间原因,这里给出的一些做法不能涵盖所有做法。

  首先是能拿到0.5分(1≤m≤30,0≤n≤m)的做法,最普通的递归。每次查询复杂度是指数级的。

#include<cstdio>
using namespace std;
const int INF=1000000007;
long long C(int m,int n)
{
if(n==0||m==n)
return 1;
return (C(m-1,n)+C(m-1,n-1))%INF;
}
int main()
{
int m,n;
while(~scanf("%d%d",&m,&n))
printf("%lld\n",C(m,n));
}

  其次是能拿到0.8分(1≤m≤10^3,0≤n≤m)的做法,即把递归的中间过程用数组记录下来(记忆化搜索),或者干脆直接dp。复杂度上,预处理O(m×n),查询O(1)。

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=1005;
const int INF=1000000007;
long long C[MAXN][MAXN];
void init()
{
C[0][0]=1;
for(int i=1; i<MAXN; ++i)
{
C[i][0]=1;
for(int j=1; j<=min(i,MAXN-1); ++j)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%INF;
}
}
int main()
{
init();
int m,n;
while(~scanf("%d%d",&m,&n))
printf("%lld\n",C[m]
);
}

  接下来是能拿到1分满分(1≤m≤5×10^5,0≤n≤m)的做法。

  李晨豪的做法。他首先通过Euler筛法线性处理出素数表,然后将每次询问进行质因数分解,快速幂再求和就可以了;质因数分解的方法是,对这个组合数的三个阶乘,分别筛出每个阶乘的质因数个数。预处理复杂度O(m),查询复杂度不太好分析,上界大概是O(m)(也可能是O(mlogm),我不确定);基本上是因为数据组数小(10组左右)水过去的。

//by 李晨豪

#include <cstdio>
#include <iostream>
using namespace std;
const long long MAXN = 1e9 + 7;
int n,m;
long long ans = 0;
int numx = 0;
long long f[60100];
bool prime[501000];

void pri()
{
for (int i = 2; i <= 501000; i++)
{
if (prime[i] == 0)
f[++numx] = i;
for (int j = 1; j <= numx && i * f[j] <= 500000; j++)
{
prime[i * f[j]] = 1;
if (i % f[j] == 0) break;
}
}

}
void calc(long long & x, long long a,int b)
{
while(b)
{
if(b&1)
{
x = (x * a) % MAXN;
}
a = (a * a) % MAXN;
b >>= 1;
}
}
int ca(long long x,long long p)
{
long long aans = 0;
long long rec = p;
while(x>=rec)
{
aans += x/rec;
rec *= p;
}
return aans;
}
int main()
{
pri();
while (~scanf("%d %d",&n,&m))
{
ans = 1;
for (int i = 1; i <= numx; i++)
{
int tmp = ca((long long)n,f[i]) - ca((long long)m,f[i])-ca((long long)(n-m),f[i]);
calc(ans,f[i],tmp);
}
printf("%lld\n",ans);
}
}

  户建坤的做法。首先算出n!求模的结果,以及(m-n+1)乘到m并求模的结果,然后因为求模的存在,不能直接用后者除以前者,所以用扩展gcd拿出前者关于模的乘法逆元,然后两者相乘即可。查询复杂度O(n),其中阶乘是O(n),扩展gcd是O(logn);因为数据组数(查询次数)小,这是目前代码中跑得最快的。

//by 户建坤

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <ctype.h>
#include <cstdlib>
#include <algorithm>

using namespace std;

const long long maxn = 1000000007;

long long extgcd(long long a,long long b,long long&x,long long&y)
{
long long d,t;
if(b==0)
{
x = 1;
y = 0;
return a;
}
d = extgcd(b,a%b,x,y);
t = x;
x = y;
y = t-a/b*y;
return d;
}
long long get_mod(long long a, long long b)
{
long long ans = 1;
for(long long x = a;x <= b;x++)
{
ans = (x*ans)%maxn;
}
return ans;
}
long long m, n;
long long mm, nn;
long long x, y;
int main()
{
while(scanf("%lld %lld", &m, &n) != EOF)
{
mm = get_mod(m - n + 1, m);
nn = get_mod(1, n);
extgcd(nn,maxn,x,y);
x*=mm;
x = x%maxn;
if(x<0)
x+=maxn;
printf("%lld\n", x);
}
return 0;
}

  何玥的做法。Lucas定理,专门用于解决组合数求模问题,虽然我知道这个定理但是不会用也不打算用……证明比较复杂,可以在网上搜一下。复杂度同样不好分析,大概每次查询O(mlogm)的样子,我不是很清楚。

//by 何玥

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
typedef long long lld;
lld  n, m, p;

lld Ext_gcd(lld a,lld b,lld &x,lld &y){
if(b==0) { x=1, y=0; return a; }
lld ret= Ext_gcd(b,a%b,y,x);
y-= a/b*x;
return ret;
}
lld Inv(lld a,int m){
lld d,x,y,t= (lld)m;
d= Ext_gcd(a,t,x,y);
if(d==1) return (x%t+t)%t;
return -1;
}

lld Cm(lld n, lld m, lld p)
{
lld a=1, b=1;
if(m>n) return 0;
while(m)
{
a=(a*n)%p;
b=(b*m)%p;
m--;
n--;
}
return (lld)a*Inv(b,p)%p;
}

int Lucas(lld n, lld m, lld p)
{
if(m==0) return 1;
return (lld)Cm(n%p,m%p,p)*(lld)Lucas(n/p,m/p,p)%p;
}

int main()
{

while(scanf("%lld%lld",&n,&m)!=EOF)
{
printf("%d\n",Lucas(n,m,1000000007));
}
return 0;
}

  最后是我的做法……线性预处理出阶乘以及阶乘关于模的逆元,然后相乘即可;其中线性预处理逆元感谢昂神指点(我以前的板子是利用费马小定理做快速幂,复杂度是O(mlogm)的)。预处理复杂度O(m),查询复杂度O(1),是满分做法中理论复杂度最优且代码最短的……(身为ACMer必然是各种板子都写过了Orz)

//by wjfwzzc

#include<cstdio>
using namespace std;
const int MAXN=500005;
const int INF=1000000007;
long long fac[MAXN],invfac[MAXN];
void init()
{
fac[0]=fac[1]=invfac[0]=invfac[1]=1;
for(int i=2; i<MAXN; ++i)
{
fac[i]=fac[i-1]*i%INF;
invfac[i]=(INF-INF/i)*invfac[INF%i]%INF;
}
for(int i=2; i<MAXN; ++i)
invfac[i]=invfac[i-1]*invfac[i]%INF;
}
inline long long C(int m,int n)
{
if(m<0||n<0||m<n)
return 0;
return fac[m]*invfac
%INF*invfac[m-n]%INF;
}
int main()
{
init();
int m,n;
while(~scanf("%d%d",&m,&n))
printf("%lld\n",C(m,n));
}

  以上就是全部题解了。如果做练习赛的人能够再多一些就好了……(大概我又想多了?)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: