比赛地址:http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=17898#overview
第1题;判断四个长度不一的边能否构成三角形。。。
分析:纯粹判断题,排序后更简单。。。
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int a[4],c; int solve() { if(a[0]+a[1]>a[2])return 1; if(a[1]+a[2]>a[3])return 1; if(a[0]+a[1]==a[2])return 2; if(a[1]+a[2]==a[3])return 2; return 0; } int main() { while(~scanf("%d%d%d%d",&a[0],&a[1],&a[2],&a[3])) { sort(a,a+4); c=solve(); if(c==0)puts("IMPOSSIBLE"); if(c==1)puts("TRIANGLE"); if(c==2)puts("SEGMENT"); } return 0; }
分析:直接枚举相邻的两个字母,判断其中是否有给定的字母,有的话就标记两个人。。。注意边界,我偷懒导致wa了
代码:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int mm=111; char map[mm][mm],ps; int p[333]; int i,j,n,m,ans; int main() { while(~scanf("%d%d %c",&n,&m,&ps)) { for(i=0;i<n;++i) scanf("%s",map[i]); memset(p,0,sizeof(p)); p[ps]=1; for(i=0;i<n;++i) for(j=0;j<m;++j) { if(map[i][j]==ps) { if(j)p[map[i][j-1]]=1; if(i)p[map[i-1][j]]=1; } if(i&&map[i-1][j]==ps)p[map[i][j]]=1; if(j&&map[i][j-1]==ps)p[map[i][j]]=1; } ans=-1; for(i='A';i<='Z';++i)ans+=p[i]; printf("%d\n",ans); } return 0; }
分析:直接开两个指针,还有统计两个人所花的时间,直接模拟即可。。。
代码:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int mm=111111; int t[mm]; int i,j,n,ta,tb; int main() { while(~scanf("%d",&n)) { for(i=1;i<=n;++i) scanf("%d",&t[i]); ta=tb=0; i=1,j=n; while(i<=j) { if(ta<=tb)ta+=t[i++]; else tb+=t[j--]; } printf("%d %d\n",i-1,n-i+1); } return 0; }
分析:数据较小,所以我立马想到可以用搜索搞,考虑按顺序来搞,第一个人只能通过第二个位置来搞定,所以第二个位置至少要放置的火球数已知,考虑普通情况下,前一个人还没死,那么至少需要的火球数也可以计算,所以可以枚举每一个位置的火球数来模拟计算,加个最优化剪枝直接搞定。。。当然也有DP的解法。。。
代码:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int h[22],t[22],s[22],out[22]; int i,a,b,n,ans; int count(int s,int a) { int ret=0; while(s>=0)s-=a,++ret; return ret; } void dfs(int l,int sum) { if(sum>=ans)return; int i,tmp; if(l==n) { tmp=max(count(h[n]-s[n],b),count(h[n-1]-s[n-1],a)); if(ans>sum+tmp) { ans=sum+tmp; for(i=2;i<n;++i)out[i]=t[i]; out[n-1]+=tmp; } return; } tmp=count(h[l-1]-s[l-1],b); if(tmp+sum>=ans)return; s[l-1]+=b*(tmp-1),s[l]+=a*(tmp-1),s[l+1]+=b*(tmp-1); for(i=tmp;i<44;++i) { s[l-1]+=b,s[l]+=a,s[l+1]+=b,t[l]=i; dfs(l+1,sum+i); } s[l-1]-=b*(i-1),s[l]-=a*(i-1),s[l+1]-=b*(i-1); } int main() { while(~scanf("%d%d%d",&n,&a,&b)) { for(i=1;i<=n;++i) scanf("%d",&h[i]); ans=2e9; memset(s,0,sizeof(s)); memset(t,0,sizeof(t)); dfs(2,0); printf("%d\n",ans); for(i=2;i<n;++i) while(out[i]--)printf("%d ",i); puts(""); } return 0; }
分析:看到n的范围,应该都知道至少要O(nlogn)的算法,普通的方法就是枚举区间两个端点,[i,j]然后判断这个区间是否满足条件,这样明显超时了,由于题目的特殊行,如果[i,j]满足条件,那么[i+1,j]必然满足条件,这个有什么用呢?我们每次整加i的时候,不用重新枚举j的范围了,呵呵,但是问题又来了,刚才那种方法可以在枚举j的时候找到最值,现在怎么办?当然得用线段树之类的区间最值求法了。。。
所以方案就是枚举加线段树,哈哈
代码:
#include<cstdio> #include<iostream> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const int mm=111111; int ma[mm<<2],mi[mm<<2],ql[mm],qr[mm]; int i,j,k,n,ans,t; void build(int l,int r,int rt) { if(l==r) { scanf("%d",&ma[rt]); mi[rt]=ma[rt]; return ; } int m=(l+r)>>1; build(lson); build(rson); ma[rt]=max(ma[rt<<1],ma[rt<<1|1]); mi[rt]=min(mi[rt<<1],mi[rt<<1|1]); } int queryma(int L,int R,int l,int r,int rt) { if(L<=l&&R>=r)return ma[rt]; int m=(l+r)>>1,ret=0; if(L<=m)ret=max(ret,queryma(L,R,lson)); if(R>m)ret=max(ret,queryma(L,R,rson)); return ret; } int querymi(int L,int R,int l,int r,int rt) { if(L<=l&&R>=r)return mi[rt]; int m=(l+r)>>1,ret=2e9; if(L<=m)ret=min(ret,querymi(L,R,lson)); if(R>m)ret=min(ret,querymi(L,R,rson)); return ret; } int main() { while(~scanf("%d%d",&n,&k)) { build(1,n,1); ans=t=0; for(i=j=1;j<=n;++i) { if(j<i)j=i; while(j<=n&&(queryma(i,j,1,n,1)-querymi(i,j,1,n,1)<=k))++j; if(j-i>ans)ans=j-i,ql[0]=i,qr[0]=j-1,t=1; else if(j-i==ans)ql[t]=i,qr[t++]=j-1; } printf("%d %d\n",ans,t); for(i=0;i<t;++i)printf("%d %d\n",ql[i],qr[i]); } return 0; }