这周趁着有Div3想上上分,过了3题,900多名。本以为这下上1400稳了吧,没想到最后一看Rating,1399.。。。看来想变成青名还得继续打喽(希望别再掉下去)。
言归正传,我们来看一下这次Div3的题解
题目链接:http://codeforces.com/contest/988
A题
这题确实比较水,由于输出任意一组就可以,那么就暴力了,使用used数组标记一下之前出现的元素就可以喽~
#include<bits/stdc++.h> using namespace std; int main() { int n,k; int cnt=0; int a[10000]; int used[10000]; int res[10000]; int u=0; int flag=0; scanf("%d%d",&n,&k); for(int i=0;i<n;i++) scanf("%d",&a[i]); for(int i=0;i<n;i++) { if(!used[a[i]]) { cnt++; used[a[i]]=1; res[u++]=i+1; } if(cnt==k) { flag=1; break; } } if(flag==0) { printf("NO\n"); } else { printf("YES\n"); printf("%d",res[0]); for(int i=1;i<u;i++) printf(" %d",res[i]); printf("\n"); } return 0; }
B题
应该是考字符串排序。若前一个能成为后一个的子串,那么前一个串的长度必定不大于后一个,根据这个首先排个序。
接下来就是字符串匹配了。可以暴力(因为串长度只有100),当然神犇也可以用KMP(觉得这道题写KMP稍微有些浪费时间)。
#include<bits/stdc++.h> using namespace std; string a[110]; bool cmp(string x,string y) { return x.size()<y.size(); } int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++) cin >> a[i]; sort(a,a+n,cmp); if(n==1) { printf("YES\n"); cout << a[0] <<endl; } else { int fff=1; for(int i=0;i<n-1;i++) { int ff=0; for(int j=0;j<=a[i+1].size()-a[i].size();j++) { int flag=1; int u=0; for(int k=j;k<j+a[i].size();k++) { if(a[i][u]!=a[i+1][k]) { flag=0; break; } else u++; } if(flag==1) { ff=1; break; } } if(ff==0) { fff=0; break; } } if(fff==1) { cout << "YES" << endl; for(int i=0;i<n;i++) cout << a[i] << endl; } else { printf("NO\n"); } } return 0; }
C题
这道题比赛的时候用了很长时间才做出来,归根结底还是map用的不熟。
思路就是在线处理,每次读入一列数就把这列数去掉一个元素后可能的情况存入map。我开了两个map,一个存第几组,一个存在每组数中的位置,也就是下标。(后来看了网上的代码可以用pair存,窝太弱了。。。)每次读入一组新的数时,就看之前有没有就行了。
#include<bits/stdc++.h> using namespace std; typedef long long ll; map<ll,int> mp1; map<ll,int> mp2; ll a[200010]; int main() { int n,r1,r2,r3,r4,t; int flag=0; scanf("%d",&t); for(ll i=1;i<=t;i++) { scanf("%d",&n); ll sum=0; for(ll j=1;j<=n;j++) { scanf("%lld",&a[j]); sum+=a[j]; } if(flag==1) continue; for(ll j=1;j<=n;j++) { map<ll,int>::iterator it=mp1.find(sum-a[j]); if(it!=mp1.end()) { if(mp1[sum-a[j]]!=i) { r1=mp1[sum-a[j]]; r2=mp2[sum-a[j]]; r3=i; r4=j; flag=1; break; } } else { mp1[sum-a[j]]=i; mp2[sum-a[j]]=j; } } } if(flag==0) printf("NO\n"); else { printf("YES\n"); printf("%d %d\n",r1,r2); printf("%d %d\n",r3,r4); } return 0; }
D题
可以说是一道数学题,也可以说是思维题
关键在于最多答案只有三个数且此时三个数成等差数列且公差为2的幂。(证明过程参考这位博主的博客)
https://blog.csdn.net/weixin_39453270/article/details/80548442
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll a[200010]; set<ll> s; int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%lld",&a[i]); s.insert(a[i]); } ll num=1; int u=0; ll res[3]; ll pre[100]; while(num<=2e9) { pre[u++]=num; num*=2; } int tot=0; int t1,t2; for(int i=0;i<n;i++) { if(tot==2) break; for(int j=0;j<u;j++) { int tem=0; t1=s.count(a[i]+pre[j]); t2=s.count(a[i]+2*pre[j]); if(t1&&t2) tem=2; else { if(t1||t2) { tem=1; } else tem=0; } if(tem>tot) { if(tem==1) { res[0]=a[i]; if(t1) res[1]=a[i]+pre[j]; if(t2) res[1]=a[i]+2*pre[j]; } else { res[0]=a[i]; res[1]=a[i]+pre[j]; res[2]=a[i]+2*pre[j]; } tot=tem; } } } if(tot==0) { printf("1\n%lld\n",a[0]); } else if(tot==1) { printf("2\n"); printf("%lld %lld\n",res[0],res[1]); } else { printf("3\n"); printf("%lld %lld %lld\n",res[0],res[1],res[2]); } return 0; }
E题
最开始想用BFS做,后来感觉过不了。后来看了别人的博客,发现是贪心。
关键在于能被25整除,那么末尾必是“00”,“25”,“50”。“75”。(这个应该很好理解)
那么我们就检查末两位,如果不是就把前面的数换到后面就好了,最后再对前导0处理一下就ok。详见代码
#include<bits/stdc++.h> typedef long long ll; using namespace std; string a; string b; int len; int tot; void make(char x,char y) { int t1=0,t2=0,f=0; if(a[len-1]==y) { t2=0; f++; } else { for(int j=len-2;j>=0;j--) { if(a[j]==y) { for(int k=j;k<=len-2;k++) { swap(a[k],a[k+1]); t2++; } f++; break; } } } if(a[len-2]==x) { t1=0; f++; } else { for(int j=len-3;j>=0;j--) { if(a[j]==x) { for(int k=j;k<=len-3;k++) { swap(a[k],a[k+1]); t1++; } f++; break; } } } int ind; int ff=0; if(f==2) { if(a[0]!='0') tot=min(tot,t1+t2); else { for(int i=1;i<=len-3;i++) { if(a[i]!='0') { ind=i; ff=1; break; } } if(ff==1) { tot=min(tot,t1+t2+ind); } } } } int main() { cin >> a; b=a; len=a.size(); tot=1000000000; if(len==1) printf("-1\n"); else { make('0','0'); a=b; make('2','5'); a=b; make('5','0'); a=b; make('7','5'); if(tot==1000000000) printf("-1\n"); else printf("%d\n",tot); } return 0; }
F题
又是dp题
参考了别人的博客,这道题有两种做法
(可以看看https://www.cnblogs.com/HDUjackyan/p/9125241.html这位大佬博客上的讲解,这里就直接搬过来了)
第一种
dp[i][j]表示从0到i时,在点i有第j把伞(没有伞时j==0)时的最小疲惫值
转移时,分成三种情况(即对应着三种操作)
dp[i+1][j]=min(dp[i+1][j],dp[i][j]+w[j]) (第i+1个点下雨,同时没带伞时不成立,其他条件都成立) 该操作是从第i个点到第i+1个点啥也不做,即第i+1个点的状态和第i个点的状态相同
dp[i+1][0]=min(dp[i+1][0],dp[i][j]) (当下一个点没下雨时成立) 该操作是在第i个点放下伞
dp[i+1][id[i]]=min(dp[i+1][id[i]],dp[i][j]+w[id[i]]) (在第i个点有伞) 该操作是第i个点有伞,拿起伞走到第i+1个点上去
#include<bits/stdc++.h> #define INF 10000000000 using namespace std; typedef long long ll; typedef struct { int ind; int w; } U; U ubr[2010]; int rain[2010]; int ok[2010]; ll dp[2010][2010]; bool cmp(U x,U y) { if(x.ind!=y.ind) return x.ind<y.ind; else return x.w<y.w; } int main() { int a,n,m,t1,t2; scanf("%d%d%d",&a,&n,&m); for(int i=1;i<=n;i++) { scanf("%d%d",&t1,&t2); for(int j=t1+1;j<=t2;j++) { rain[j]=1; } } ubr[0].ind=-1; ubr[0].w=0; for(int i=1;i<=m;i++) { scanf("%d%d",&ubr[i].ind,&ubr[i].w); } sort(ubr,ubr+m+1,cmp); for(int i=1;i<=m;i++) { if(ok[ubr[i].ind]==0) ok[ubr[i].ind]=i; } for(int i=0;i<=a;i++) { for(int j=0;j<=m;j++) { dp[i][j]=INF; } } dp[0][0]=0; for(int i=0;i<a;i++) { for(int j=0;j<=m;j++) { if(j||!rain[i+1]) dp[i+1][j]=min(dp[i+1][j],dp[i][j]+ubr[j].w); if(!rain[i+1]) dp[i+1][0]=min(dp[i+1][0],dp[i][j]); if(ok[i]) dp[i+1][ok[i]]=min(dp[i+1][ok[i]],dp[i][j]+ubr[ok[i]].w); } } ll ans=INF; for(int j=0;j<=m;j++) ans=min(ans,dp[a][j]); if(ans==INF) printf("-1\n"); else printf("%lld\n",ans); return 0; }
第二种
这种我只是看了一下就口胡了,没有写。看完之后感觉甚至比第一种好理解?(想看代码的话直接看上面的博客就行)