Codeforces Gym 2015 ACM Amman Collegiate Programming Contest
比賽連結:
題目連結:
A. Who Is The Winner?直接排序,複雜度O(nlogn)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<string> using namespace std; struct team { string name; int s,p; bool operator < (const team &t)const { return s==t.s ? p<t.p : s>t.s; } }t[105]; int main() { ios::sync_with_stdio(false); int T; cin>>T; while(T--) { int N; cin>>N; for(int i=1;i<=N;i++) { cin>>t[i].name>>t[i].s>>t[i].p; } sort(t+1,t+N+1); cout<<t[1].name<<endl; } return 0; }
B. Rock-Paper-Scissors
分別預處理前k(k=0,1,2,...,n)次均出剪刀、石頭或布的得分,O(n^2)列舉分界點即可。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<string> using namespace std; char s[1005]; int cnt[3][1005]; int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); scanf("%s",s); for(int i=1;i<=n;i++) { cnt[0][i]=cnt[0][i-1]+(s[i-1]=='S')-(s[i-1]=='P'); cnt[1][i]=cnt[1][i-1]+(s[i-1]=='R')-(s[i-1]=='S'); cnt[2][i]=cnt[2][i-1]+(s[i-1]=='P')-(s[i-1]=='R'); } int ans=0; for(int i=0;i<=n;i++) { for(int j=i;j<=n;j++) { //printf("%d %d\n",i,j); if(cnt[0][i]+cnt[1][j]-cnt[1][i]+cnt[2][n]-cnt[2][j]>0)ans++; } } printf("%d\n",ans); } return 0; }
C. Street Lamps
將所有被路燈找到的格子標記為"*",於是每一段連續的k個"."對答案的貢獻為ceil(k/3),複雜度O(n)。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<string> using namespace std; char s[105]; bool isok[105]; int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); scanf("%s",s); memset(isok,0,sizeof(isok)); for(int i=0;i<n;i++) { if(i>0 && s[i-1]=='*')isok[i]=1; if(s[i]=='*')isok[i]=1; if(i<n-1 && s[i+1]=='*')isok[i]=1; } int loc=0,cnt=0,ans=0; while(loc<n) { while(loc<n && isok[loc]) { ans+=(cnt+2)/3; cnt=0; loc++; } if(loc<n) { cnt++; loc++; } } ans+=(cnt+2)/3; printf("%d\n",ans); } return 0; }
D. Alternating Strings
記dp[i]為將序列前i個字元按照要求劃分所需要的最少段數,
若子串s[j..(i-1)]為“交替串”,則有dp[i]=max(dp[i],dp[j]+(i-j)),否則有dp[i]=max(dp[i],dp[j]+1),
暴力列舉j轉移即可,複雜度O(n^2)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1005;
char s[MAXN];
int dp[MAXN];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k;
scanf("%d%d",&n,&k);
scanf("%s",s);
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
{
bool isok=0;
for(int j=i-1;j>=max(0,i-k);j--)
{
if(j<i-1 && s[j]==s[j+1])isok=1;
if(isok)dp[i]=min(dp[i],dp[j]+1);
else dp[i]=min(dp[i],dp[j]+(i-j));
}
}
printf("%d\n",dp[n]-1);
}
return 0;
}
E. Epic Professor
找出最大的數k,將所有數加上100-k之後統計>=50的數的個數即可,複雜度O(n)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int a[105];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
int mm=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
mm=max(mm,a[i]);
}
int add=100-mm;
int ans=0;
for(int i=1;i<=n;i++)
{
if(a[i]+add>=50)ans++;
}
printf("%d\n",ans);
}
return 0;
}
F. Travelling Salesman
最小瓶頸生成樹,跑一遍Kruskal即可,複雜度O(mlogm)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=100005;
const int MAXM=100005;
struct Edge
{
int u,v,c;
bool operator < (const Edge &t)const
{
return c<t.c;
}
}e[MAXM];
int p[MAXN];
void Init(int n)
{
for(int i=1;i<=n;i++)p[i]=i;
}
int Find(int x)
{
return x==p[x] ? x : p[x]=Find(p[x]);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c);
sort(e,e+m);
int ans=0,dif=n;
Init(n);
for(int i=0;i<m;i++)
{
int t1=Find(e[i].u);
int t2=Find(e[i].v);
if(t1!=t2)
{
p[t1]=t2;
dif--;
}
ans=max(ans,e[i].c);
if(dif==1)break;
}
printf("%d\n",ans);
}
return 0;
}
G. Heavy Coins
暴力列舉所有硬幣構成的集合,統計從該集合刪去任意一枚硬幣後總價值<=S的方案數,複雜度O(n*2^n)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
int a[15];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,s;
scanf("%d%d",&n,&s);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
int ans=0;
for(int mask=0;mask<(1<<n);mask++)
{
int tot=0,cnt=0;
for(int i=0;i<n;i++)
if(mask&(1<<i))
{
cnt++;
tot+=a[i];
}
if(tot>=s)
{
bool isok=1;
for(int i=0;i<n;i++)
if(mask&(1<<i))
if(tot-a[i]>=s)isok=0;
if(isok)ans=max(ans,cnt);
}
}
printf("%d\n",ans);
}
return 0;
}
H. Bridges
對原圖做一次雙連通分量縮點,對縮點後得到樹做兩次BFS找出直徑,該直徑的長度即為最多可以減少的割邊數,複雜度O(n+m)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int MAXN=100005;
const int MAXM=200005;
struct Edge
{
int to,next;
bool cut;
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index,top;
int block;
bool Instack[MAXN];
int bridge;
void addedge(int u,int v)
{
edge[tot].to=v;
edge[tot].next=head[u];
edge[tot].cut=0;
head[u]=tot++;
}
void Tarjan(int u,int pre)
{
int v;
Low[u]=DFN[u]=++Index;
Stack[top++]=u;
Instack[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(v==pre)continue;
if(!DFN[v])
{
Tarjan(v,u);
if(Low[u]>Low[v])Low[u]=Low[v];
if(Low[v]>DFN[u])
{
bridge++;
edge[i].cut=1;
edge[i^1].cut=1;
}
}
else if(Instack[v] && Low[u]>DFN[v])
Low[u]=DFN[v];
}
if(Low[u]==DFN[u])
{
block++;
do
{
v=Stack[--top];
Instack[v]=0;
Belong[v]=block;
}
while(v!=u);
}
}
vector<int>e[MAXN];
void init(int n)
{
for(int i=1;i<=n;i++)e[i].clear();
tot=0;
memset(head,-1,sizeof(head));
}
int dis[MAXN];
void bfs(int st)
{
queue<int>q;
q.push(st);
dis[st]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0;i<e[u].size();i++)
{
if(dis[e[u][i]]>dis[u]+1)
{
q.push(e[u][i]);
dis[e[u][i]]=dis[u]+1;
}
}
}
}
void solve(int n)
{
memset(DFN,0,sizeof(DFN));
memset(Instack,0,sizeof(Instack));
Index=top=block=bridge=0;
Tarjan(1,0);
for(int i=1;i<=n;i++)
for(int j=head[i];j!=-1;j=edge[j].next)
if(edge[j].cut)
{
e[Belong[edge[j].to]].push_back(Belong[i]);
e[Belong[i]].push_back(Belong[edge[j].to]);
}
memset(dis,0x3f3f3f3f,sizeof(dis));
bfs(1);
int ans=0,loc=1;
for(int i=1;i<=block;i++)
{
if(dis[i]>ans)
{
loc=i;
ans=dis[i];
}
}
memset(dis,0x3f3f3f3f,sizeof(dis));
bfs(loc);
ans=0;
for(int i=1;i<=block;i++)ans=max(ans,dis[i]);
printf("%d\n",bridge-ans);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
init(n);
int u,v;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
solve(n);
}
return 0;
}
I. Bahosain and Digits
列舉k,列舉操作完成後所有位的結果,利用標記保證每一次檢驗的效率,複雜度O(10*n^2)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
char t[255],s[255];
int lazy[255];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",t);
int n=strlen(t);
int res=0;
bool isok=0;
for(int k=n;k>=1 && !isok;k--)
{
for(int d=0;d<=9 && !isok;d++)
{
strcpy(s,t);
memset(lazy,0,sizeof(lazy));
int cur=0;
for(int i=0;i<=n-k;i++)
{
s[i]=(s[i]-'0'+cur%10+10)%10+'0';
lazy[i]+=d+10-(s[i]-'0');
lazy[i+k-1]-=d+10-(s[i]-'0');
cur+=lazy[i];
}
for(int i=n-k+1;i<n;i++)
{
s[i]=(s[i]-'0'+cur%10+10)%10+'0';
cur+=lazy[i];
}
//printf("%s\n",s);
bool flag=1;
for(int i=n-k+1;i<n;i++)
if(s[i]-'0'!=d)flag=0;
if(flag)
{
isok=1;
res=k;
}
}
}
printf("%d\n",res);
}
return 0;
}
J. Candy
直接維護兩個優先佇列,複雜度O(nlogn+mlogm)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
map<int,int>s,p;
map<int,int>::iterator itr_s,itr_p;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
int in;
s.clear();
p.clear();
for(int i=1;i<=n;i++)
{
scanf("%d",&in);
s[in]++;
}
for(int i=1;i<=m;i++)
{
scanf("%d",&in);
p[in]++;
}
itr_s=s.begin();
itr_p=p.begin();
while(itr_s!=s.end() && itr_p!=p.end())
{
while(itr_p!=p.end() && itr_s->second > itr_p->second)p.erase(itr_p++);
if(itr_p==p.end())break;
s.erase(itr_s++);
p.erase(itr_p++);
}
printf("%s\n",(s.empty() ? "YES" : "NO"));
}
return 0;
}
K. Runtime Error
記錄每個數的出現次數,逐個列舉序列中的元素,注意x==0以及x==y的情形,複雜度O(n)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int a[100005];
int x[100005];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k;
scanf("%d%d",&n,&k);
memset(x,0,sizeof(x));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
x[a[i]]++;
}
int mm=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
if(a[i]==0)continue;
if(k%a[i]!=0)continue;
if(x[k/a[i]]>1-(k/a[i]!=a[i]))mm=min(mm,a[i]);
}
if(mm==0x3f3f3f3f)printf("-1\n");
else printf("%d %d\n",mm,k/mm);
}
return 0;
}
L. Alternating Strings II
此題是D題的加強版,
注意到“交替串”的長度具有二分性質,
如果用一個序列記錄前i個字元中有多少對相鄰的字元是不同的,
那麼對於一個固定的i,可以二分出最小的j使得s[j..(i-1)]是“交替串”,
仍考慮D題的dp方程,
利用線段樹分別維護dp[i]的最小值以及dp[i]-i的最小值,
得到分界點j之後,可以利用線段樹上的區間查詢加速轉移,
複雜度O(nlogn)。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=100005;
const int INF=0x3f3f3f3f;
char str[MAXN];
int pre[MAXN],dp[MAXN];
struct node
{
int l,r,m,v[2];
}s[MAXN<<2];
void push_up(int n)
{
for(int i=0;i<2;i++)
s[n].v[i]=min(s[n<<1].v[i],s[n<<1|1].v[i]);
}
void build(int l,int r,int n)
{
int m=(l+r)>>1;
s[n].l=l;
s[n].r=r;
s[n].m=m;
if(r-l==1)
{
s[n].v[0]=s[n].v[1]=INF;
return;
}
build(l,m,n<<1);
build(m,r,n<<1|1);
}
void update(int k,int p,int n)
{
if(s[n].l==p && s[n].r==p+1)
{
s[n].v[0]=min(s[n].v[0],k);
s[n].v[1]=min(s[n].v[1],k-(p-1));
return;
}
if(p<s[n].m)update(k,p,n<<1);
else update(k,p,n<<1|1);
push_up(n);
}
int query(int l,int r,int n,int op)
{
if(r<=l)return INF;
if(s[n].l==l && s[n].r==r)return s[n].v[op];
if(r<=s[n].m)return query(l,r,n<<1,op);
if(l>=s[n].m)return query(l,r,n<<1|1,op);
return min(query(l,s[n].m,n<<1,op),query(s[n].m,r,n<<1|1,op));
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k;
scanf("%d%d",&n,&k);
scanf("%s",str);
pre[0]=0;
for(int i=1;i<n;i++)
pre[i]=pre[i-1]+(str[i]!=str[i-1]);
memset(dp,INF,sizeof(dp));
build(1,n+2,1);
//printf("build done\n");
dp[0]=0;
update(dp[0],1,1);
for(int i=1;i<=n;i++)
{
//printf("work on %d\n",i);
int l=max(i-k,0),r=i-1;
while(l<r)
{
int m=(l+r)>>1;
if(pre[i-1]-pre[m]==i-m-1)r=m;
else l=m+1;
}
//printf("left most %d\n",l);
dp[i]=min(dp[i],query(max(i-k,0)+1,l+1,1,0)+1);
dp[i]=min(dp[i],query(l+1,i+1,1,1)+i);
//printf("%d %d %d\n",query(max(i-k,0)+1,l+2,1,0),query(l+2,i+1,1,1),dp[i]);
update(dp[i],i+1,1);
}
printf("%d\n",dp[n]-1);
}
return 0;
}