Gym-101653:acific Northwest Regional Contest (2019训练第一场)
本套题没有什么数据结构题,图论题,唯一有价值的就是Q题博弈,在最后面,读者可以直接拉到最下面。
(还剩下两个,估计每什么价值的题,懒得补了
M .Polyhedra
pro:欧拉公式,V-E+F=2;给定V,E,求F
sol:F=E-V+2;
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; int main() { int T; scanf("%d", &T); while(T--) { int a, b; scanf("%d%d", &a, &b); printf("%d\n", b - a + 2); } return 0; }
N .Majority
pro: 给定一些一些1到1000的数字,问最小的出现次数最多的数字。
sol: 模拟。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; int num[maxn]; int main() { int T,N,x; scanf("%d",&T); while(T--){ scanf("%d",&N); rep(i,1,1000) num[i]=0; rep(i,1,N){ scanf("%d",&x); num[x]++; } int ans=0,x=-1; rep(i,1,1000) { if(num[i]>x) ans=i,x=num[i]; } printf("%d\n",ans); } return 0; }
O .Diamonds
pro:给定一些二元组,求最长的序列,满足a递增,b递减。
sol:N^2暴力即可。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; struct in{ double x,y; }s[maxn]; int dp[210]; int main() { int T,N,ans; scanf("%d",&T); while(T--){ scanf("%d",&N); ans=1; rep(i,1,N) scanf("%lf%lf",&s[i].x,&s[i].y); rep(i,1,N){ dp[i]=1; rep(j,1,i-1){ if(s[j].x<s[i].x&&s[j].y>s[i].y) dp[i]=max(dp[i],dp[j]+1); } ans=max(ans,dp[i]); } printf("%d\n",ans); } return 0; }
R .Ramp Number
pro:一个数字X是合法的,当且仅当各位数字从左到右不降。 如果一个数字的合法,求多少个小于他的数是合法的。
sol:基本数位DP。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=100; ll dp[maxn][10]; char c[maxn]; int a[maxn],len; ll dfs(int pos,int lim,int now) { if(pos==len) return 1; if(!lim&&dp[pos][now]) return dp[pos][now]; ll res=0; int up=9; if(lim) up=a[pos+1]; rep(j,now,up) res+=dfs(pos+1,lim&&j==a[pos+1],j); return dp[pos][now]=res; } void solve() { ll res=0; rep(i,1,len){ memset(dp,0,sizeof(dp)); int up=9; if(i==1) up=a[1]; rep(j,1,up) res+=dfs(i,i==1&&j==up,j); } printf("%lld\n",res); } int main() { int T; scanf("%d",&T); while(T--){ scanf("%s",c+1); len=strlen(c+1); rep(i,1,len) a[i]=c[i]-'0'; bool F=true; rep(i,2,len) if(a[i]<a[i-1]) F=false; if(!F){ puts("-1"); continue;} solve(); } return 0; }
X .Wrench
pro:把一个带小数的数字表示为整数 加一个 分数形式,满足分母是2的幂次,而且要越小的满足的,允许有最后一位的误差。
sol:模拟,细节需要注意。
#include<bits/stdc++.h> using namespace std; int t,n; char s[1010],s1[100]; int main(){ for(scanf("%d",&t);t--;){ scanf("%s",s);n=strlen(s); int p=0;bool flag=0,flag1=0; while(s[p]=='0'&&p<n)p++; while(s[p]!='.'&&p<n)putchar(s[p++]),flag1=1; for(int i=p+1;i<n;i++) if(s[i]!='0'){flag=1;break;} if(flag){if(flag1)putchar(' ');} else{puts("\"");continue;} flag=0; for(int i=2;i<=128;i<<=1){ for(int j=1;j<i;j++){ double x=1.0*j/i; for(int v=0;v<8;v++)x*=10,s1[v]=(int)x%10+'0'; bool f=1; for(int k=1;p+k<n;k++) if(s1[k-1]!=s[p+k]&&(p+k<n-1||s1[k-1]+1!=s[p+k])){f=0;break;} if(f){printf("%d/%d\"\n",j,i);flag=1;break;} } if(flag)break; } } return 0; }
U .Top 25
题意:给定A和B,分别是不同的1到N的排列,找到连续的段(越短越好),代表的集合相同。
思路:每次找到对应的位置,如果最远的对应位置和当前相同,说明这段集合相同。
Map+clear: 3525ms
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; map<string, int>Map; int f[maxn]; vector<int>ans; int main() { int T; scanf("%d", &T); while(T--) { int N,x,y; Map.clear(); string s; scanf("%d",&N); rep(i,1,N){ cin>>s; Map[s]=i; } rep(i,1,N){ cin >> s; f[Map[s]] = i; } rep(i,1,N){ if(i==f[i]) printf("1 "); else{ int maxx = f[i]; for(int j = i + 1; j <= maxx; j++){ if(f[j] > maxx)maxx = f[j]; } printf("%d ",maxx-i+1); i = maxx; } } puts(""); } return 0; }
Map: 4227ms
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; map<string, int>Map; int f[maxn]; vector<int>ans; int main() { int T; scanf("%d", &T); while(T--) { int N,x,y; string s; scanf("%d",&N); rep(i,1,N){ cin>>s; Map[s]=i; } rep(i,1,N){ cin >> s; f[Map[s]] = i; } rep(i,1,N){ if(i==f[i]) printf("1 "); else{ int maxx = f[i]; for(int j = i + 1; j <= maxx; j++){ if(f[j] > maxx)maxx = f[j]; } printf("%d ",maxx-i+1); i = maxx; } } puts(""); } return 0; }
unordered_map:2479ms
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; unordered_map<string, int>Map; int f[maxn]; int main() { int T; scanf("%d", &T); while(T--) { Map.clear(); int N,x,y; string s; scanf("%d",&N); rep(i,1,N){ cin>>s; Map[s]=i; } rep(i,1,N){ cin >> s; f[Map[s]] = i; } rep(i,1,N){ if(i==f[i]) printf("1 "); else{ int maxx = f[i]; for(int j = i + 1; j <= maxx; j++){ if(f[j] > maxx)maxx = f[j]; } printf("%d ",maxx-i+1); i = maxx; } } puts(""); } return 0; }
这告诉我们map的效率和map的大小有关,所以要及时的clear。 而我如果用set,一位set一直在erase,元素会慢慢表少,所以二分的速度会更快。
set: 3010ms
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; set<string>s; string a[maxn],b[maxn]; int main() { int T; scanf("%d", &T); while(T--) { int N,x,y,tot=0; scanf("%d",&N); rep(i,1,N) cin>>a[i]; rep(i,1,N) cin>>b[i]; rep(i,1,N) { tot++; if(s.find(a[i])!=s.end()) s.erase(a[i]); else s.insert(a[i]); if(s.find(b[i])!=s.end()) s.erase(b[i]); else s.insert(b[i]); if(s.empty()) { printf("%d ",tot); tot=0; } } puts(""); } return 0; }
(但是主要的时间还是在输入输出那里。
W .Wormhole
pro:给定三维的N个点,有一些点对可以互通,其他的点对距离是欧几里得距离。Q次询问点对最近距离。
sol:folyd。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=100; typedef long long ll; struct node { double x, y, z; double operator - (const node& a)const { return (sqrt((x - a.x) * (x - a.x) + (y - a.y) * (y - a.y) + (z - a.z) * (z - a.z))); } }a[maxn]; map<string, int>Map; double dis[100][100]; int main() { int T, cases = 0; scanf("%d", &T); while(T--) { memset(dis, 0, sizeof(dis)); Map.clear(); int n, m; scanf("%d", &n); string s, s1; for(int i = 1; i <= n; i++) { cin >> s >> a[i].x >> a[i].y >> a[i].z; Map[s] = i; } for(int i = 1; i <= n; i++) { for(int j = i + 1; j <= n; j++) { dis[i][j] = dis[j][i] = a[i] - a[j]; //cout<<dis[i][j]<<endl; } } cin >> m; while(m--) { cin >> s >> s1; dis[Map[s]][Map[s1]] = 0; } for(int k = 1; k <= n; k++) for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); cin >> m; cout<<"Case "<<++cases<<":\n"; while(m--) { cin >> s >> s1; cout<<"The distance from "<<s<<" to "<<s1<<" is "<<(ll)(dis[Map[s]][Map[s1]]+0.5)<<" parsecs.\n"; } } return 0; }
V .Towers
pro:给定N,让填数独,然后给出(N+2)*(N+2)的矩阵,里面N*N的数字或者未知,周围4行表示从那个方向看过去的LIS。N<=5
sol:搜索,需要及时减枝。 check函数就是给个方向的判定。 只跑了30ms,估计包本来就可以暴力一点,即到了边界再减枝?
减枝1:对于每填一个数,就看和上面的数字是否有重复。
减枝2:对于每个数,看左边和上边看过去的LIS已经超过题目描述,退出。
判定3:对于右边界和下边界,检查从右和下看过去的LIS。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=130; char c[9][9],ans[9][9]; int vis[9][9],N; int get(int x,int y,int tx,int ty,int X,int Y) { int res=0,now=0; while(1){ x+=tx; y+=ty; if(ans[x][y]>now) now=ans[x][y],res++; if(x==X&&y==Y) return res; } return res; } bool check(int u,int v) { rep(i,1,u-1) if(ans[u][v]==ans[i][v]) return false; if(c[u][0]!='-'){ int t=get(u,0,0,1,u,v); if(t>c[u][0]-'0') return false; if(v==N&&t!=c[u][0]-'0') return false; } if(c[0][v]!='-'){ int t=get(0,v,1,0,u,v); if(t>c[0][v]-'0') return false; if(u==N&&t!=c[0][v]-'0') return false; } if(v==N&&c[u][N+1]!='-'){ if(get(u,N+1,0,-1,u,1)!=c[u][N+1]-'0') return false; } if(u==N&&c[N+1][v]!='-'){ if(get(N+1,v,-1,0,1,v)!=c[N+1][v]-'0') return false; } return true; } bool dfs(int u,int v) { if(u==N+1&&v==1) { return true; } int L=1,R=N; if(c[u][v]!='-') L=R=c[u][v]-'0'; rep(i,L,R){ if(!vis[u][i]){ vis[u][i]=1; ans[u][v]=i; if(v<N){ if(check(u,v)){ if(dfs(u,v+1)) return true; } } else{ if(check(u,v)) { if(dfs(u+1,1)) return true; } } vis[u][i]=0; } } return false; } int main() { int T; scanf("%d",&T); while(T--){ scanf("%d",&N); memset(vis,0,sizeof(vis)); rep(i,0,N+1) scanf("%s",c[i]); if(!dfs(1,1)) puts("no\n"); else { rep(i,1,N) { rep(j,1,N) putchar(ans[i][j]+'0'); puts(""); } puts(""); } } return 0; }
T .Runes
pro:给定等式,其中有不超过6个问号,让你填一个相同的未出现过的数字,使等式满足。
sol:枚举。注意负号,0等情况。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=100; typedef long long ll; bool judge(char s[], int x, ll& y) { int n = strlen(s); char s1[110]; memcpy(s1, s, sizeof(s1)); for(int i = 0; i < n; i++) { if(s1[i] == '?')s1[i] = '0' + x; } if(s1[0] == '-' && s1[1] == '0')return false; if(s1[0] == '0' && n != 1)return false; bool flag; if(s1[0] == '-')flag = 1; else flag = 0; y = 0; for(int i = flag; i < n; i++)y = y * 10 + s1[i] - '0'; if(flag)y = -y; } char s[110], s1[110], s2[110], s3[110]; int vis[15]; int main() { int T, cases = 0; scanf("%d", &T); while(T--) { memset(s1, 0, sizeof(s1)); memset(s2, 0, sizeof(s2)); memset(s3, 0, sizeof(s3)); memset(vis, 0, sizeof(vis)); scanf("%s", s); int n = strlen(s), tmp, tmp1; for(int i = 0; i < n; i++) { if((isdigit(s[i]) || s[i] == '?') && (s[i + 1] == '+' || s[i + 1] == '-' || s[i + 1] == '*')) { tmp = i + 1; break; } } for(int i = 0; i < n; i++)if(s[i] == '='){tmp1 = i;break;} for(int i = 0; i < n; i++)if(isdigit(s[i]))vis[s[i] - '0'] = 1; for(int i = 0; i < tmp; i++)s1[i] = s[i]; for(int i = tmp + 1, j = 0; i < tmp1; i++, j++)s2[j] = s[i]; for(int i = tmp1 + 1, j = 0; i < n; i++, j++)s3[j] = s[i]; bool ok = 0; for(int i = 0; i <= 9; i++) { if(vis[i])continue; ll a, b, c; bool flag = 0; if(judge(s1, i, a) && judge(s2, i, b) && judge(s3, i, c)) { if(s[tmp] == '-' && a - b == c)flag = 1; if(s[tmp] == '+' && a + b == c)flag = 1; if(s[tmp] == '*' && a * b == c)flag = 1; } if(flag) { ok = 1; printf("%d\n", i); break; } } if(!ok)printf("-1\n"); } return 0; }
Q .Number Game
pro:双人博弈,给定一个N的排列,Alice先手,Bob后手,他们轮流取,取到数字1的胜,能取走一个数字的条件是两旁没有比它大的数字。
sol:我们发现,只有和1相邻的连续区间是要考虑的; 之外的区间因为一定可以按一定顺序取完,所以只考虑奇偶性。
即用dp[L][R][k]保存先手的情况:[L,R]是当前包含数字1的区间,k是此区间外的未取的个数奇偶性。然后就可以记忆化搜索了。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=110; int dp[maxn][maxn][2],a[maxn],pos; int dfs(int L,int R,int k) { if(L==R) return 1; if(dp[L][R][k]!=-1) return dp[L][R][k]; if(k==1&&!dfs(L,R,0)) dp[L][R][k]=1; rep(i,L,R){ bool F=true; if(i-1>=L&&a[i-1]>a[i]) F=false; if(i+1<=R&&a[i+1]>a[i]) F=false; if(!F) continue; if(i==pos) dp[L][R][k]=1; else { if(i<pos){ if(!dfs(i+1,R,(k+i-L)&1)) dp[L][R][k]=1; } else { if(!dfs(L,i-1,(k+R-i)&1)) dp[L][R][k]=1; } } } if(dp[L][R][k]==-1) dp[L][R][k]=0; return dp[L][R][k]; } int main() { int N,T; scanf("%d",&T); while(T--){ scanf("%d",&N); rep(i,1,N){ scanf("%d",&a[i]); if(a[i]==1) pos=i; } memset(dp,-1,sizeof(dp)); dfs(1,N,0); if(dp[1][N][0]) puts("Alice"); else puts("Bob"); } return 0; }