时间:2017/9/8 题目8/10 Rank 5/150
体会:三星的题目和国内区域赛差距大,大多数题读懂题意就能做,所以静心读题是关键,套路性太深。
A:
题意:给出一个算式,算式中的数字用大写字母代替。每个字母只能代替一个数字,一个数字也只能被一个字母代替。有多少种数字分配方式可以使得这个算式成立?
解法:爆搜。一共不超过十个字母,把这十个字母列出来然后进行dfs分配数字,分配完后就进行验证是否可以满足式子,还要考虑累加的n-1个数不能是前导0,。
#include <bits/stdc++.h> using namespace std; typedef long long LL; int len[20], n, cnt; char str[20][20]; bool used[30]; int vis[30]; struct node{ char ch; int num, flag;//flag标记前导0 }a[20]; bool check() { int sum = 0; for(int i=1; i<n; i++){ int t = 0; for(int j=0; j<len[i]; j++) t = t*10 + vis[str[i][j]-'A']; sum += t; } int ans = 0; for(int i=0; i<len[n]; i++) ans = ans*10 + vis[str[n][i]-'A']; return sum == ans; } LL dfs(int pos) { LL ans=0; if(pos>=cnt) return check(); for(int i=0; i<=9; i++){ if(used[i]==false){ if(a[pos].flag == 0 && i == 0) continue; a[pos].num = i; vis[a[pos].ch-'A'] = i; used[i] = 1; ans += dfs(pos+1); used[i] = 0; } } return ans; } int main() { while(~scanf("%d", &n)) { cnt = 0; memset(vis, 0, sizeof(vis)); for(int i=1; i<=n; i++){ scanf("%s", str[i]); len[i] = strlen(str[i]); for(int j=0; j<len[i]; j++){ if(!vis[str[i][j]-'A']){ a[cnt].flag = 1; a[cnt++].ch = str[i][j], vis[str[i][j]-'A'] = 1; } if(j==0){ for(int k=0; k<cnt; k++){ if(a[k].ch == str[i][0]){ a[k].flag = 0; } } } } } LL ans = 0; memset(used,false,sizeof(used)); ans = dfs(0); printf("%lld ", ans); } return 0; }
B:
题意:计算 1 和 n 两点间最短路的路径和的两倍
解法:计算最短路, 枚举没一条边到两端的距离加上本身长度,判断是等于最短路长度,若是的,那么这条边可以当做最短路的一条边,注意好像spfa会TLE。我是看图猜的题意,
导致很快就写出了这题,以后比赛要注意,猜错容易崩盘。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int maxn = 10005; const int maxm = 250000 * 2; struct EDGE { int to, next, len; EDGE() {} EDGE(int to, int next, int len) :to(to), next(next), len(len) {} }edge[maxm]; int head[maxn],edgecnt; int dis[2][maxn]; int n; struct node { int u, dis; node(int u,int dis):u(u),dis(dis){} bool operator < (const node &b) const { return dis > b.dis; } }; void init() { memset(head, -1, sizeof(head)); edgecnt = 0; } void add(int s, int t, int l) { edge[edgecnt] = EDGE(t, head[s], l); head[s] = edgecnt++; } void spfa(int st, int dis[]) { for (int i = 1; i <= n; i++) dis[i] = INT_MAX; dis[st] = 0; priority_queue<node> q; static bool f[maxn]; memset(f, 0, sizeof(f)); q.emplace(st, 0); while (!q.empty()) { int u = q.top().u; q.pop(); if (f[u]) continue; f[u] = 1; for (int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; if (dis[v] > dis[u] + edge[i].len) { dis[v] = dis[u] + edge[i].len; q.emplace(v, dis[v]); } } } } int main() { int m; while (~scanf("%d %d", &n, &m)) { init(); for (int i = 1; i <= m; i++) { int s, t, l; scanf("%d %d %d", &s, &t, &l); s++; t++; add(s, t, l); add(t, s, l); } spfa(1, dis[0]); spfa(n, dis[1]); int len = dis[0][n]; int ans = 0; for (int u = 1; u <= n; u++) { for (int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].to; if (v == u) continue; if (dis[0][u] + edge[i].len + dis[1][v] == len) ans += edge[i].len; } } printf("%d ", 2 * ans); } return 0; }
C:
题意:给你N个整数和M个整数,问这M个数中,有几个数可以表达成那N个整数中一个或者两个整数的和。
解法:简单FFT即可。发现我的FFT大概慢100ms,下次要注意。
#include <bits/stdc++.h> using namespace std; const double PI = acos(-1); const int maxn = 800010; typedef complex <double> Complex; void rader(Complex *y, int len) { for(int i = 1, j = len / 2; i < len - 1; i++) { if(i < j) swap(y[i], y[j]); int k = len / 2; while(j >= k) {j -= k; k /= 2;} if(j < k) j += k; } } void fft(Complex *y, int len, int op) { rader(y, len); for(int h = 2; h <= len; h <<= 1) { double ang = op * 2 * PI / h; Complex wn(cos(ang), sin(ang)); for(int j = 0; j < len; j += h) { Complex w(1, 0); for(int k = j; k < j + h / 2; k++) { Complex u = y[k]; Complex t = w * y[k + h / 2]; y[k] = u + t; y[k + h / 2] = u - t; w = w * wn; } } } if(op == -1) for(int i = 0; i < len; i++) y[i] /= len; } Complex x1[maxn], x2[maxn]; int n, q, a[maxn]; int sum[maxn]; int main() { while(~scanf("%d", &n)) { memset(a, 0, sizeof(a)); int len1 = 0; for(int i=0; i<n; i++){ int x; scanf("%d", &x); a[x]=1; len1 = max(len1, x); } len1++; int len=1; while(len<len1*2) len<<=1; a[0] = 1; for(int i=0; i<len1; i++) x1[i] = Complex(a[i], 0); for(int i=0; i<len1; i++) x2[i] = Complex(a[i], 0); fft(x1, len, 1); fft(x2, len, 1); for(int i=0; i<len; i++) x1[i] = x1[i]*x2[i]; fft(x1, len, -1); for(int i=0; i<len; i++) sum[i] = (int)(x1[i].real()+0.5); scanf("%d", &q); int ans = 0; while(q--) { int x; scanf("%d", &x); if(sum[x]>0) ans++; } printf("%d ", ans); } return 0; }
D:
题意:在一个借书会中,若A喜欢B的书,B喜欢C的书,C喜欢A的书,这样每个人都可以借到书了,输出 yes,若 A喜欢B的书,B喜欢C的书,C喜欢D的书,这样就不能保证每个人都借到书,输出 no
解法:这个可以看做二分匹配,将每一个点拆分为两个点A B,自己与自己不连边,添加一个源点和一个汇点,源点到A的流的大小为 1 B到汇点的流的大小为 1 ,问是否可以匹配成功让大家都借到书,直接匈牙利也是可以的。一眼想到二分匹配,但是队友因为数据范围说不可能,自己有一点点动摇,然后写个网络流。下次要注意,不能全听队友,要坚持自己。
#include <bits/stdc++.h> using namespace std; const int maxn = 100100; const int maxm = 400100; const int inf = 0x3f3f3f3f; struct G { int v, cap, next; G() {} G(int v, int cap, int next) : v(v), cap(cap), next(next) {} } E[maxm]; int p[maxn], T; int d[maxn], temp_p[maxn], qw[maxn]; //d顶点到源点的距离标号,temp_p当前狐优化,qw队列 void init() { memset(p, -1, sizeof(p)); T = 0; } void add(int u, int v, int cap) { E[T] = G(v, cap, p[u]); p[u] = T++; E[T] = G(u, 0, p[v]); p[v] = T++; } bool bfs(int st, int en, int n) { int i, u, v, head, tail; for(i = 0; i <= n; i++) d[i] = -1; head = tail = 0; d[st] = 0; qw[tail] = st; while(head <= tail) { u = qw[head++]; for(i = p[u]; i + 1; i = E[i].next) { v = E[i].v; if(d[v] == -1 && E[i].cap > 0) { d[v] = d[u] + 1; qw[++tail] = v; } } } return (d[en] != -1); } int dfs(int u, int en, int f) { if(u == en || f == 0) return f; int flow = 0, temp; for(; temp_p[u] + 1; temp_p[u] = E[temp_p[u]].next) { G& e = E[temp_p[u]]; if(d[u] + 1 == d[e.v]) { temp = dfs(e.v, en, min(f, e.cap)); if(temp > 0) { e.cap -= temp; E[temp_p[u] ^ 1].cap += temp; flow += temp; f -= temp; if(f == 0) break; } } } return flow; } int dinic(int st, int en, int n) { int i, ans = 0; while(bfs(st, en, n)) { for(i = 0; i <= n; i++) temp_p[i] = p[i]; ans += dfs(st, en, inf); } return ans; } int n, m; int main() { while(~scanf("%d %d", &n,&m)){ init(); int st = 2*n; int en = st+1; for(int i=1; i<=m; i++){ int u, v; scanf("%d %d", &u,&v); add(u,v+n,1); } for(int i=0; i<n; i++) add(st,i,1); for(int i=n; i<2*n; i++) add(i,en,1); int ans = dinic(st,en,en+1); if(ans==n) puts("YES"); else puts("NO"); } return 0; }
E:
题意:有n个机器人(不超过4个),机器人都是要碰到障碍物或者机器人才会停下来的,不然就沿原方向一直走下去,4个机器人都可以移动,求最少移动多少步,机器人1可以到达出口(X)
解法:爆搜,BFS,题目中已经限定了步数,这就是可行性剪枝,难点在于如何表示矩形的状态,我们没有必要把矩形完全表示出来,直接把机器人拿出来即可,把位置拿出来做成一个int的Hash值,然后爆搜即可。队友写这个题,调试时间比较久,需要注意。
#include <bits/stdc++.h> using namespace std; int n, h, w, l, ex, ey; const int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}}; char g[20][20]; bool vis[100000000]; bool check(int x, int y){ if(x>=1&&x<=w&&y>=1&&y<=h&&g[x][y]=='.') return true; else return false; } struct node{ int step; int x[5], y[5]; int Hash(){ int ans = 0; for(int i=1; i<=n; i++) ans = ans*10 + x[i]; for(int i=1; i<=n; i++) ans = ans*10 + y[i]; return ans; } }now; void BFS() { now.step = 0; queue <node >q; q.push(now); vis[now.Hash()] = 1; while(q.size()) { now = q.front(); q.pop(); if(now.step>l) continue; if(now.x[1]==ex&&now.y[1]==ey){ printf("%d ", now.step); return; } node Next; for(int i=1; i<=n; i++) g[now.x[i]][now.y[i]] = i+'0'; for(int i=1; i<=n; i++){ g[now.x[i]][now.y[i]] = '.'; for(int j=0; j<4; j++){ Next = now; Next.step++; while(check(Next.x[i]+dir[j][0], Next.y[i]+dir[j][1])){ Next.x[i]+=dir[j][0]; Next.y[i]+=dir[j][1]; } if(!vis[Next.Hash()]){ vis[Next.Hash()] = 1; q.push(Next); } } g[now.x[i]][now.y[i]] = i + '0'; } for(int i=1; i<=n; i++) g[now.x[i]][now.y[i]] = '.';//这里一定要改回来,因为x[i]和y[i]已经变化了 } printf("NO SOLUTION "); } int main() { scanf("%d %d %d %d", &n,&h,&w,&l); for(int i=1; i<=w; i++){ scanf("%s", g[i]+1); for(int j=1; j<=h; j++){ if(g[i][j]>='1'&&g[i][j]<='4'){ now.x[g[i][j]-'0'] = i; now.y[g[i][j]-'0'] = j; g[i][j] = '.'; } else if(g[i][j] == 'X'){ ex = i; ey = j; g[i][j] = '.'; } } } BFS(); return 0; }
F:
题意:给出一些矩阵,如果矩阵之间有接触算作一个连通分量,问最大的连通分量的面积为多少?
解法:既然接触即算为一个连通分量,那么我们可以分别讨论他的横边接触和竖边接触。将横边和竖边分别存储在不同的结构体数组中,然后对数组进行排序,将互相接触的边对应的矩阵放在统一并查集中。最后统计下所有并查集的面积大小,输出最大的一个即可。
#include <bits/stdc++.h> using namespace std; const int maxn = 50010; const int inf = 0x3f3f3f3f; namespace DSU{ int fa[maxn]; void init(){ for(int i=1; i<maxn; i++) fa[i]=i; } int find_set(int x){ if(x==fa[x]) return x; else return fa[x] = find_set(fa[x]); } void union_set(int x, int y){ x = find_set(x), y = find_set(y); if(x!=y){ fa[x] = y; } } } using namespace DSU; struct node{ int x, y, w, h; }nodes[maxn]; struct line{ int x,l,r,pos; bool operator<(const line &rhs) const{ return (x<rhs.x||x==rhs.x&&l<rhs.l||x==rhs.x&&l==rhs.l&&r<rhs.r); } }a[maxn*2], b[maxn*2]; //a存水平线段,b存垂直线段 int sum[maxn]; int main() { int n; while(~scanf("%d", &n)) { init(); for(int i=1; i<=n; i++){ scanf("%d %d %d %d", &nodes[i].x,&nodes[i].y,&nodes[i].w,&nodes[i].h); //水平 a[i].x = nodes[i].y, a[i].l = nodes[i].x, a[i].r = nodes[i].x+nodes[i].w, a[i].pos = i; a[i+n].x = nodes[i].y+nodes[i].h, a[i+n].l = nodes[i].x, a[i+n].r = nodes[i].x+nodes[i].w, a[i+n].pos = i; //垂直 b[i].x = nodes[i].x, b[i].l = nodes[i].y, b[i].r = nodes[i].y+nodes[i].h, b[i].pos = i; b[i+n].x = nodes[i].x + nodes[i].w, b[i+n].l = nodes[i].y, b[i+n].r = nodes[i].y+nodes[i].h, b[i+n].pos = i; } sort(a+1, a+2*n+1); sort(b+1, b+2*n+1); memset(sum, 0, sizeof(sum)); int m = 2*n; // for(int i=1,j; i<=m; ){ int tx = a[i].r; j = i+1; while(j<=m&&a[j].x==a[i].x&&a[j].l<=tx){ tx = max(tx, a[j].r); union_set(a[i].pos, a[j].pos); j++; } i=j; } // for(int i=1,j; i<=m; ){ int tx = b[i].r; j=i+1; while(j<=m&&b[j].x==b[i].x&&b[j].l<=tx){ tx = max(tx, b[j].r); union_set(b[i].pos, b[j].pos); j++; } i=j; } for(int i=1; i<=n; i++){ sum[find_set(i)] += nodes[i].h*nodes[i].w; } int ans = 0; for(int i=1; i<=n; i++){ ans = max(ans, sum[i]); } printf("%d ", ans); } return 0; }
G:
题意:给出两个图,通过缩小,问两个图是否是相同的,缩小的时候不是按比例,比如说两行间距为三可以缩小为二,为6可以缩小为5
解法:读这个题的时候我就知道是矩阵离散化了,然后写完离散化丢给队友写暴力旋转4个方向判相等,除了离散化开始写多了点东西,基本没什么问题。
#include <bits/stdc++.h> using namespace std; const int maxn = 3010; int W1, H1, N; int W2, H2; int X1[maxn], X2[maxn], Y1[maxn], Y2[maxn]; int X3[maxn], Y3[maxn], X4[maxn], Y4[maxn]; void Rotate() { int ymax = *max_element(Y1, Y1+N); for(int i=0; i<N; i++){ int x = ymax - Y1[i]; int y = X1[i]; X1[i] = x; Y1[i] = y; } } bool check() { //第一个离散化后矩形的最左下角的值 int p1 = 0; for(int i=1; i<N; i++){ if(make_pair(X1[i], Y1[i]) < make_pair(X1[p1], Y1[p1])){ p1 = i; } } //第一个离散化后矩形的最左下角的值 int p2 = 0; for(int i=1; i<N; i++){ if(make_pair(X3[i],Y3[i]) < make_pair(X3[p2], Y3[p2])){ p2 = i; } } for(int i=0; i<N; i++){ int q1 = (p1+i)%N; int q2 = (p2+i)%N; if(X1[q1] != X3[q2] || Y1[q1] != Y3[q2]) return false; } return true; } //对x1和x2进行坐标离散化,并且返回离散化后的宽度 int compress(int *x1, int *x2, int w) { vector <int> xs(x2, x2 + N); sort(xs.begin(), xs.end()); xs.erase(unique(xs.begin(), xs.end()), xs.end()); for(int i = 0; i < N; i++) { x1[i] = lower_bound(xs.begin(), xs.end(), x1[i]) - xs.begin(); x2[i] = lower_bound(xs.begin(), xs.end(), x2[i]) - xs.begin(); } return xs.size(); } int main() { while(~scanf("%d", &N)) { //ZXY W1 = 0; H1 = 0; for(int i = 0; i < N; i++) { scanf("%d %d", &X1[i], &Y1[i]); X2[i] = X1[i], Y2[i] = Y1[i]; W1 = max(W1, X1[i]); H1 = max(H1, Y1[i]); } W1 = compress(X1, X2, W1); H1 = compress(Y1, Y2, H1); W2 = 0; H2 = 0; scanf("%d", &N); for(int i = 0; i < N; i++) { scanf("%d %d", &X3[i], &Y3[i]); X4[i] = X3[i], Y4[i] = Y3[i]; W2 = max(W2, X3[i]); H2 = max(H2, Y3[i]); } W2 = compress(X3, X4, W2); H2 = compress(Y3, Y4, H2); //LYY if(check()) { puts("yes"); continue; } Rotate(); if(check()) { puts("yes"); continue; } Rotate(); if(check()) { puts("yes"); continue; } Rotate(); if(check()) { puts("yes"); continue; } puts("no"); } return 0; }
H: 留坑
I:给了一些数字和字符,字符里面有问号,这个序列是个圆形,我们可以从任意一个数字开始重新启动一个序列,现在序列里面每个?可以替换成+,-,*任意一个,且优先级随便考虑,现在问这n次移动构成的n个序列的最小值和最大值是多少,输出每一轮的最小最大的绝对值,拼成一个字符串,最后输出一个空行。n<=200。
解法:把字符串copy一份,变成长度400的字符串,对这个序列做一个区间DP即可。转移很简单,看代码吧。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<cstring> using namespace std; typedef long long LL; const int maxn = 405; LL mx[maxn][maxn]; LL mi[maxn][maxn]; void getMaxMin(const vector<int> &num, const vector<char> &symbol) { memset(mx, 0x80, sizeof(mx)); memset(mi, 0x7F, sizeof(mi)); int n = num.size(); for(int i = 0; i < n; i++) { mi[i][i] = mx[i][i] = num[i]; } for(int len = 2; len <= n / 2; len++) { for(int st = 0; st + len <= n; st++) { int en = st + len - 1; for(int mid = st; mid < en; mid++) { if(symbol[mid] == '+' || symbol[mid] == '?') { mi[st][en] = min(mi[st][en], mi[st][mid] + mi[mid + 1][en]); mx[st][en] = max(mx[st][en], mx[st][mid] + mx[mid + 1][en]); } if(symbol[mid] == '-' || symbol[mid] == '?') { mi[st][en] = min(mi[st][en], mi[st][mid] - mx[mid + 1][en]); mx[st][en] = max(mx[st][en], mx[st][mid] - mi[mid + 1][en]); } if(symbol[mid] == '*' || symbol[mid] == '?') { mi[st][en] = min(mi[st][en], mi[st][mid] * mi[mid + 1][en]); mi[st][en] = min(mi[st][en], mi[st][mid] * mx[mid + 1][en]); mi[st][en] = min(mi[st][en], mx[st][mid] * mi[mid + 1][en]); mi[st][en] = min(mi[st][en], mx[st][mid] * mx[mid + 1][en]); mx[st][en] = max(mx[st][en], mx[st][mid] * mx[mid + 1][en]); mx[st][en] = max(mx[st][en], mx[st][mid] * mi[mid + 1][en]); mx[st][en] = max(mx[st][en], mi[st][mid] * mx[mid + 1][en]); mx[st][en] = max(mx[st][en], mi[st][mid] * mi[mid + 1][en]); } } } } } int main() { int n; while(~scanf("%d", &n)) { vector<int> num; vector<char> symbol; for(int i = 0; i < n; i++) { int x; scanf("%d", &x); num.push_back(x); char s[10]; scanf("%s", s); symbol.push_back(s[0]); } for(int i = 0; i < n; i++) { num.push_back(num[i]); symbol.push_back(symbol[i]); } getMaxMin(num, symbol); for(int i = 0; i < n; i++) { printf("%lld%lld", abs(mi[i][i + n - 1]), abs(mx[i][i + n - 1])); } printf(" "); } return 0; }
J:
题意:给一个小矩形,再给一个大矩形,问小矩形在大矩形出现的次数。
解法:矩阵Hash。双值Hash,模数取成121和131即可过。
#include <bits/stdc++.h> using namespace std; const unsigned int BASE1 = 121; const unsigned int BASE2 = 131; const int mod = 99999997; const int maxn = 2010; int n, m, a, b, q; unsigned int hash1[maxn][maxn], hash2[maxn][maxn]; unsigned int bas1[maxn], bas2[maxn]; unsigned int getHash() { for (int i = 1; i <= a; i++) for (int j = 1; j <= b; j++) hash2[i][j] += hash2[i - 1][j] * BASE1; for (int i = 1; i <= a; i++) { for (int j = 1; j <= b; j++) { hash2[i][j] += hash2[i][j - 1] * BASE2; } } return hash2[a][b]; } char str1[maxn]; int main() { while (~scanf("%d %d %d %d", &a, &b, &n, &m)) { for (int i = 1; i <= a; i++) { scanf("%s", str1+1); for (int j = 1; j <= b; j++) { if (str1[j] == 'o') { hash2[i][j] = 1; } else { hash2[i][j] = 0; } } } unsigned int des = getHash(); bas1[0] = bas2[0] = 1; for (int i = 1; i <= 1010; i++) bas1[i] = bas1[i - 1] * BASE1, bas2[i] = bas2[i - 1] * BASE2; for (int i = 1; i <= n; i++) { scanf("%s", str1+1); for (int j = 1; j <= m; j++) { if (str1[j] == 'o') { hash1[i][j] = 1; } else { hash1[i][j] = 0; } } } int ans = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { hash1[i][j] += hash1[i - 1][j] * BASE1; } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { hash1[i][j] += hash1[i][j - 1] * BASE2; } } for(int i=a; i<=n; i++){ for(int j=b; j<=m; j++){ unsigned int h = hash1[i][j]; h -= hash1[i-a][j]*bas1[a]; h -= hash1[i][j-b]*bas2[b]; h += hash1[i-a][j-b]*bas1[a]*bas2[b]; if(h==des){ ans++; } } } printf("%d ", ans); } return 0; }