SME 2019 ACM 题解
感谢梁晖学长为了组织这场竞赛而做的努力
A
搜索
2^15
#include <cstdio> #include <algorithm> #define ll long long using namespace std; ll n,x,d,s[16],c[16],ans=0; void dfs(ll idx) { if (idx==n+1) { ll smax=0,smin=1e9,empty=1,ssum=0; for (ll i=1;i<=n;i++) { if (c[i]==1) { smax=max(smax,s[i]); smin=min(smin,s[i]); ssum+=s[i]; empty=0; } } if (empty==0&&smax-smin<=d&&ssum>=x) ans++; return; } c[idx]=0; dfs(idx+1); c[idx]=1; dfs(idx+1); } int main() { scanf("%lld%lld%lld",&n,&x,&d); for (ll i=1;i<=n;i++) scanf("%lld",&s[i]); dfs(1); printf("%lld",ans); return 0; }
B
横纵坐标之差绝对值的和
#include <cstdio> #include <algorithm> int t, x1, fucky1, x2, y2; int main() { scanf("%d", &t); for (int i = 1; i <= t; i++) { scanf("%d%d%d%d", &x1, &fucky1, &x2, &y2); printf("%d\n", abs(x1 - x2) + abs(fucky1 - y2)); } }
C
贪心
如果当前位置到目标位置的向量在当前朝向上有正投影则前进,否则右转
#include <cstdio> #include <algorithm> int x, y, xd, yd, dir; char ch; int ans(int xc, int yc, int dir, int out) { int cnt = 0; for (int i = 0; i < 4; i++) { if ((dir == 0 && yc > 0) || (dir == 2 && yc < 0)) { if (out) printf("A %d\n", abs(yc)); yc = 0; cnt++; } if ((dir == 1 && xc > 0) || (dir == 3 && xc < 0)) { if (out) printf("A %d\n", abs(xc)); xc = 0; cnt++; } if (yc != 0 || xc != 0) { if (out) printf("D\n"); cnt++; } dir = (dir + 1) % 4; } return cnt; } int main() { //0N,1E,2S,3W scanf("%d%d %c%d%d", &x, &y, &ch, &xd, &yd); if (ch == 'N') dir = 0; if (ch == 'E') dir = 1; if (ch == 'S') dir = 2; if (ch == 'O') dir = 3; int xc, yc; xc = xd - x; yc = yd - y; printf("%d\n", ans(xc, yc, dir, 0)); ans(xc, yc, dir, 1); }
D
判断字符ASCII加一,特判z到a
#include <cstdio> #include <iostream> #include <string> using namespace std; int t, len; string s; int main() { scanf("%d", &t); for (int i = 1; i <= t; i++) { int ans = 1; cin >> s; len = s.length(); for (int i = 0; i < len - 1; i++) { if (!(s[i] + 1 == s[i + 1] || (s[i] == 'z'&&s[i + 1] == 'a'))) ans = 0; } if (ans) printf("YES\n"); else printf("NO\n"); } }
E
贪心
优先队列
#include <cstdio> #include <algorithm> #include <queue> #include<functional> using namespace std; int t, n, k, x, sum; priority_queue<int, vector<int>, greater<int> > heap; int main() { scanf("%d", &t); for (int i = 1; i <= t; i++) { sum = 0; scanf("%d%d", &n, &k); for (int j = 1; j <= n; j++) { scanf("%d", &x); heap.push(x); } for (int j = 1; j <= k; j++) { x = heap.top(); heap.pop(); heap.push(-x); } for (int j = 1; j <= n; j++) { x = heap.top(); heap.pop(); sum += x; } printf("%d\n", sum); } }
F
模拟
注意判断初始方向,防止第一步走到界外
#include <cstdio> int t, k, l1, r1, p1, d1, l2, r2, p2, d2, ans; int main() { scanf("%d", &t); for (int i = 1; i <= t; i++) { ans = 0; scanf("%d%d%d%d", &l1, &r1, &p1, &d1); scanf("%d%d%d%d", &l2, &r2, &p2, &d2); d1 = d1 ? 1 : -1; d2 = d2 ? 1 : -1; scanf("%d", &k); for (int j = 1; j <= k; j++) { if (p1 == l1) d1 = 1; if (p1 == r1) d1 = -1; if (p2 == l2) d2 = 1; if (p2 == r2) d2 = -1; p1 += d1; p2 += d2; if (p1 == p2) ans++; } printf("%d\n", ans); } }
G
升高选定对象1单位,降低其他对象1单位
等价于升高选定对象2单位,其他对象不变
所以满足任意对象高度差能被2整除则满足条件
从左到右扫描一次即可
#include <cstdio> int t,n,h[100001],ans; int main() { scanf("%d",&t); for (int i=1;i<=t;i++) { ans=1; scanf("%d",&n); for (int j=1;j<=n;j++) scanf("%d",&h[j]); for (int j=1;j<=n-1;j++) if ((h[j]-h[j+1])%2!=0) ans=0; if (ans) printf("yes\n");else printf("no\n"); } }
H
博弈论
通过手玩可知
n=1是p状态
n=2,3是n状态
n大于等于4是p状态,证明如下
当n是偶数,先手选取中间的2个可以等分队列
当n是奇数,先手选取中间的3个可以等分队列
对于已经被先手等分的队列,接下来先手只要镜像后手的操作则一定可以先涂黑最后的白块从而获得胜利
#include <cstdio> int t,n; int main() { scanf("%d",&t); for (int i=1;i<=t;i++) { scanf("%d",&n); if (n==2||n==3) printf("second\n");else printf("first\n"); } }
I
从外到内扫描对应字符是否反转,并记录当前位置内侧子串的反转次数,即可判定当前位置是否需要反转(大雾
#include <cstdio> #include <string> #include <iostream> using namespace std; int t,len,ans,pd,re; string a,b; int main() { scanf("%d",&t); for (int j=1;j<=t;j++) { re=0; pd=0; ans=0; cin>>a; cin>>b; len=a.length(); if (a[(len-1)/2]!=b[(len-1)/2]) pd=1; for (int i=0;i<(len-1)/2;i++) { if (a[i]==b[i]&&a[len-1-i]==b[len-1-i]&&a[i]==a[len-1-i]) { }else if (a[i]==b[i]&&a[len-1-i]==b[len-1-i]) { if (re==1) { ans++; re=0; } }else if (a[i]==b[len-1-i]&&a[len-1-i]==b[i]) { if (re==0) { ans++; re=1; } }else{ pd=1; } } if (pd) printf("-1\n");else printf("%d\n",ans); } }
J
搜索
#include <cstdio> int n,x,y,a[100001],pd; int main() { scanf("%d%d%d",&n,&x,&y); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) { pd=0; for (int j=i-1;j>=i-x&&j>=1;j--) if (a[j]<=a[i]) pd=1; for (int j=i+1;j<=i+y&&j<=n;j++) if (a[j]<=a[i]) pd=1; if (pd==0) { printf("%d\n",i); break; } } }
K
只有如下一种情况不满足要求
如果同时有下#和上#却没有上下#
#include <cstdio> #include <string> #include <iostream> using namespace std; string s1,s2; int len,x,cnt[4]; int main() { cin>>s1; cin>>s2; len=s1.length(); for (int i=0;i<4;i++) cnt[i]=0; for (int i=0;i<len;i++) { x=0; if (s1[i]=='#') x+=1; if (s2[i]=='#') x+=2; cnt[x]++; } if (cnt[1]>0&&cnt[2]>0&&cnt[3]==0) { printf("NO\n"); } else { printf("YES\n"); for (int i=0;i<cnt[0];i++) printf("."); for (int i=0;i<cnt[1];i++) printf("#"); for (int i=0;i<cnt[3];i++) printf("#"); for (int i=0;i<cnt[2];i++) printf("."); printf("\n"); for (int i=0;i<cnt[0];i++) printf("."); for (int i=0;i<cnt[1];i++) printf("."); for (int i=0;i<cnt[3];i++) printf("#"); for (int i=0;i<cnt[2];i++) printf("#"); } }
L
贪心+DP
贪心策略
最快的两个人先到A区
用最快的两个人来搬运当前B区最慢的一个人或两个人并回到A区
搬运一个人的代价是a1+an
搬运两个人的代价是a1+2*a2+an
转移状态时不一定哪种方式更优
所以使用动态规划来解决
#include <cstdio> #include <cstring> #include <climits> #include <algorithm> #define ll long long using namespace std; ll n,a[100001],p[100001]; int main() { scanf("%d",&n); for (ll i=1;i<=n;i++) {scanf("%d",&a[i]);p[i]=LLONG_MAX;} if (n==1) { printf("%lld\n",a[1]); return 0; } sort(a+1,a+n+1); p[n]=a[2]; for (ll i=n;i>=3;i--) { p[i-1]=min(p[i-1],p[i]+a[1]+a[i]); p[i-2]=min(p[i-2],p[i]+a[1]+2*a[2]+a[i]); } printf("%lld\n",p[2]); }