2016年山东省acm比赛题解完全版
a Julyed
题意:n个单词,每天最多背m个单词,最少背多少天
分析:水题,不解释
#include<bits/stdc++.h> using namespace std; int main(){ int t,n,m; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); int ans=n/m; if(n%m!=0) ans++; printf("%d\n",ans); } return 0; }
b. Fibonacci
题意:把n分解为Fibonacci数列的和,并且两个数不能连续
分析:其实两个数不能连续没有用,证明一下如果n=f1+f2+x,分类讨论下,x=f2,n那么n=2*f1+f2=f2+f3=f4,如果x=f3,那么n=f1+f2+f3=f1+f4,如果x==f4 n=f1+f2+f4=f3+f4=f5,如果n>f4
n=f3+x
所以n从大到小依次减Fibonacci,然后倒序输出就ok了
#include<bits/stdc++.h> using namespace std; const int maxn=105; int d[maxn],sz; void init(){ d[0]=d[1]=1; for(sz=2;;sz++){ d[sz]=d[sz-1]+d[sz-2]; if(d[sz]>=1e9) break; } } int main(){ init(); int t,n; scanf("%d",&t); while(t--){ scanf("%d",&n); stack<int> s; int tmp=n; for(int i=sz;i>0;i--) if(d[i]<=n){ s.push(d[i]); n-=d[i]; } printf("%d=",tmp); int a=s.top();s.pop(); printf("%d",a); while(!s.empty()){ a=s.top();s.pop(); printf("+%d",a); } puts(""); } return 0; }
c.Proxy
题意:0是本地计算机,n+1是服务器,1-n是代理服务器,给出每条线路的时间消耗,然后找到最短的0到n+1的路径,然后输出0处选择的第一个代理服务器,如如果不存在,输出-1,如果0-n+1直连且最短,输出0
分析:mdzz,读错题了,哎,说多了都是泪,都出的水题我们没出,dijkstra,把边倒着输入,然后找n+1-0的最短路,松弛操作记录前驱,特殊情况判断下就ok了
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; const int INF=1e9; struct Edge{ int from,to,dist; Edge(int u,int v,int w):from(u),to(v),dist(w){} }; struct HeapNode{ int d,u; bool operator < (const HeapNode& r) const{ return d>r.d; } }; struct Dijkstra{ int n,m; vector<Edge> edges; vector<int> G[maxn]; bool done[maxn]; int d[maxn]; int p[maxn]; void init(int n){ this->n=n; for(int i=0;i<n;i++) G[i].clear(); edges.clear(); } void AddEdges(int from,int to,int dist){ edges.push_back(Edge(from,to,dist)); m=edges.size(); G[from].push_back(m-1); } void dijkstra(int s){ priority_queue<HeapNode> q; for(int i=0;i<n;i++) d[i]=INF; d[s]=0; memset(done,0,sizeof(done)); q.push((HeapNode){0,s}); while(!q.empty()){ HeapNode x=q.top();q.pop(); int u=x.u; if(done[u]) continue; done[u]=true; for(int i=0;i<G[u].size();i++){ Edge& e=edges[G[u][i]]; if(d[e.to]>d[u]+e.dist){ d[e.to]=d[u]+e.dist; p[e.to]=u; q.push((HeapNode){d[e.to],e.to}); } else if(d[e.to]==d[u]+e.dist) p[e.to]=min(p[e.to],G[u][i]); } } } }dijkstra; int main(){ int t; scanf("%d",&t); while(t--){ int n,m; scanf("%d%d",&n,&m); dijkstra.init(n+2); int u,v,w; while(m--){ scanf("%d%d%d",&u,&v,&w); dijkstra.AddEdges(v,u,w); } dijkstra.dijkstra(n+1); if(dijkstra.d[0]>=INF){ puts("-1"); continue; } if(dijkstra.p[0]==n+1){ puts("0"); continue; } printf("%d\n",dijkstra.p[0]); } return 0; }
d.Swiss-system tournament
2*n个人,每个人有一个初始的分数,和能力值,按照分数排序,分数相同,序号小的在前面,然后每次(1 2)(3 4),这样的两个人比较,能力值大的+1分,小的不变,最后输出排名为k的人的编号
分析:zz题,当时没几个出的,思路题,想不出来没办法,要注意到失败的人是有序,胜利的人是有序的,+1后依然是有序的,两个数组分别存,这不就是归并排序的定义嘛,O(n)时间完成排序,总时间复杂度O(n*logn+r*n)
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; int n,m,r; struct node{ int id,x,s; void read(int a,int b,int c){ id=a; x=b; s=c; } }v[maxn*2],v1[maxn],v2[maxn]; bool cmp(node a,node b){ if(a.x==b.x) return a.id<b.id; return a.x>b.x; } void solve(){ int l=0,r=0; for(int i=0;i<n;i+=2){ int a=i,b=i+1; if(v[a].s<v[b].s||(v[a].s==v[b].s&&v[a].id>v[b].id)) swap(a,b); v1[l++].read(v[a].id,v[a].x+1,v[a].s); v2[r++].read(v[b].id,v[b].x,v[b].s); } int a=0,b=0,c=0; while(a<l||b<r){ if((b>=r)||(a<l&&(v1[a].x>v2[b].x||(v1[a].x==v2[b].x&&v1[a].id<v2[b].id)))) v[c++]=v1[a++]; else v[c++]=v2[b++]; //printf("%d ",v[c-1].x); } // puts(""); } int main(){ int t; scanf("%d",&t); while(t--){ scanf("%d%d%d",&n,&r,&m); n*=2; for(int i=0;i<n;i++){ scanf("%d",&v[i].x); v[i].id=i; } for(int i=0;i<n;i++) scanf("%d",&v[i].s); sort(v,v+n,cmp); while(r--) solve(); printf("%d\n",v[m-1].id+1); } return 0; }
e.The Binding of Isaac
题意:有一些房间,周围都是空地,求只与一个房间相邻的空地数
分析:这个题都出了吧,看到第一眼以为是个搜索,没想到就一水题,n,m周围都搞成空地,然后输入就行,之间判断每个空地周围房间数
#include<bits/stdc++.h> using namespace std; const int maxn=105; const int dx[]={0,0,1,-1}; const int dy[]={1,-1,0,0}; char g[maxn][maxn]; int n,m; int judge(int x,int y){ int count=0; for(int i=0;i<4;i++){ int nx=x+dx[i]; int ny=y+dy[i]; if(nx<0||nx>n+1||ny<0||ny>m+1) continue; if(g[nx][ny]=='#') count++; } return count==1?1:0; } int main(){ int t; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m);getchar(); for(int i=0;i<=n+1;i++)g[i][0]=g[i][m+1]='.'; for(int i=0;i<=m+1;i++)g[0][i]=g[n+1][i]='.'; for(int i=1;i<=n;i++){ scanf("%s",g[i]+1); getchar(); } int ans=0; for(int i=0;i<=n+1;i++) for(int j=0;j<=m+1;j++) if(g[i][j]=='#') continue; else ans+=judge(i,j); printf("%d\n",ans); } return 0; }
f.Feed the monkey
题意:三个东西分别有n1,n2,n3个,每天取一个物品,但是三种物品连续取得不得超过d1,d2,d3个,问有多少取方案
分析:当时一看,算了下转态50^4*3,1e7多点,没问题,时间足够,而且非常多的状态达不到,就采用了记忆化搜索,当时脑残了,有个错就是找不到,遗憾,出来就找到了,悲哀,莫过于赛后过题
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; int dp[55][55][55][55][3]; int n[3],num[3]; int dfs(int a,int b,int c,int d,int e){ int& z=dp[a][b][c][d][e]; if(a==0&&b==0&&c==0)return z=1; if(z!=-1) return z; ll ans=0; for(int i=0;i<3;i++){ if(i==0&&a==0)continue; else if(i==1&&b==0)continue; else if(i==2&&c==0)continue; int p; if(e==i){ p=d+1; if(p>num[i])continue; } else p=1; if(i==0) ans+=dfs(a-1,b,c,p,i); else if(i==1) ans+=dfs(a,b-1,c,p,i); else if(i==2) ans+=dfs(a,b,c-1,p,i); ans%=mod; } return z=(int)ans; } int main(){ int t; cin>>t; while(t--){ for(int i=0;i<3;i++) cin>>n[i]; for(int i=0;i<3;i++) cin>>num[i]; ll ans=0; memset(dp,-1,sizeof(dp)); for(int i=0;i<3;i++){ if(n[i]==0) continue; if(i==0) ans+=dfs(n[0]-1,n[1],n[2],1,i); else if(i==1) ans+=dfs(n[0],n[1]-1,n[2],1,i); else if(i==2) ans+=dfs(n[0],n[1],n[2]-1,1,i); ans%=mod; } cout<<ans<<endl; } return 0; }
g.Triple Nim
题意:nim游戏都玩过吧,把n个数字分成三堆,两人采取最佳策略,先手输
分析:先手输,也就是说三堆异或为0,然后我就往二进制上想,先想到奇数无解,因为最后的那个1,没有其他的1和他去^,然后由此我想到对应位的1只能分解为下一位的两个1,当时没想到组合数,我蒙了一个公式,决定赌一发,wa了,然后我继续考虑n中二进制有4个1的情况,然后想到一个神奇的式子(3^3-1)/2,一试,2 3个1的时候也成立,然后就搞了,a了,赛后想了下原理,比如20,二进制分解为11110,第四位必须放两个1,然后就是剩下三个1的放法了,每个1有三种方法,也就是3^3,减去第三个数什么都没得到的情况,然后(1,3,4)和(1,4,3)是同一种情况,所以/2
#include<bits/stdc++.h> using namespace std; int main(){ int t,n; scanf("%d",&t); while(t--){ scanf("%d",&n); if(n&1){ puts("0"); continue; } int count=-1; for(int i=1;i<=31;i++) if(n&(1<<i)) count++; long long ans=pow(3,count)-1; ans/=2; printf("%lld\n",ans); } return 0; }
h.Memory Leak
题意:顾名思义,内存泄漏吗,题意就是题目图片那个题意,没啥坑,比赛的时候的还以为掉坑了,哎
分析:那就模拟呗,把三个整在一个char 数组里最好搞了,记录输入的三个数组的名字和开始结尾,然后输出的时候,就输出呗,直到遇到'\0',反正题目给了,最后肯定有'\0'结尾
当时比赛的时候,就这么写的,测试数据都过,不知道哪写错了
#include <bits/stdc++.h> using namespace std; const int MAXN = 11111; typedef pair<int, int>pii; char a[MAXN]; string tmp; string input; map<string, pii>m; int main(){ ios::sync_with_stdio(false); int T; string cmd, name; cin >> T; while (T--){ m.clear(); cin.ignore(); memset(a, 0, sizeof(a)); getline(cin, input); istringstream sin(input); sin >> tmp; int pos = 0; while (sin >> tmp){ int l = tmp.find('['), r = tmp.find(']'), length; name = tmp.substr(0, l); istringstream sz(tmp.substr(l + 1, r - l + 1)); sz >> length; m[name] = pii(pos, pos + length); pos += length; } while (cin >> cmd >> name && cmd[0] != 'r'){ pii sb = m[name]; if (cmd == "gets"){ cin.ignore(); getline(cin, input); for (int i = 0; i < input.length(); i++) if (i + sb.first < sb.second) a[i + sb.first] = input[i]; else break; if (sb.second - sb.first > input.length()) a[sb.first + input.length()] = '\0'; } else { cout << a + sb.first << endl; } } } return 0; }
iRock Paper Scissors
题意:有n个,玩石头剪刀布游戏,每个人出拳10次,每个人可以和其他的任何人玩一次,输出每个人赢0-10次的次数
分析:教练说,看上去像网络流,网络流解决一切问题,不过,看了测试代码,发现不是网络流,是hash+预处理
我先捋捋思路,一会更新
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 7; const int Limit = 243; // 3^5 int Win(int x, int y) { int ret = 0; for (int k = 0; k < 5; k ++) { int dig0 = x % 3, dig1 = y % 3; if (dig0 == (dig1 + 1) % 3) { ret ++; } x /= 3, y /= 3; } return ret; } vector<int> win[6][Limit], lose[6][Limit]; void Init() { for (int i = 0; i < Limit; i ++) { for (int j = 0; j < Limit; j ++) { win[Win(i, j)][i].push_back(j); lose[Win(i, j)][j].push_back(i); } } } int T, cas, n, a[N][11], res[N][11], f[6][Limit][Limit]; char ch[11]; int main() { Init(); cin >> T; while (T --) { scanf("%d", &n); for (int i = 0; i < n; i ++) { scanf(" %s", ch); for (int j = 0; j < 10; j ++) { if (ch[j] == 'R') a[i][j] = 0; if (ch[j] == 'P') a[i][j] = 1; if (ch[j] == 'S') a[i][j] = 2; } } memset(f, 0, sizeof(f)); memset(res, 0, sizeof(res)); int tot = 0; for (int i = 0; i < n; i ++) { int mask0 = 0, mask1 = 0; for (int j = 0; j < 5; j ++) mask0 = mask0 * 3 + a[i][j]; for (int j = 5; j < 10; j ++) mask1 = mask1 * 3 + a[i][j]; int tot = 0; for (int j = 0; j <= 5; j ++) { for(int libiao = 0;libiao<lose[j][mask0].size();libiao++){ int t = lose[j][mask0][libiao]; tot ++; f[j][t][mask1] ++; } } } for (int i = 0; i < n; i ++) { int mask0 = 0, mask1 = 0; for (int j = 0; j < 5; j ++) mask0 = mask0 * 3 + a[i][j]; for (int j = 5; j < 10; j ++) mask1 = mask1 * 3 + a[i][j]; for (int s0 = 0; s0 <= 5; s0 ++) for (int s1 = 0; s1 <= 5; s1 ++) { for(int libiao = 0;libiao < win[s1][mask1].size();libiao++){ int t = win[s1][mask1][libiao]; res[i][s0 + s1] += f[s0][mask0][t]; } } res[i][0] --; } printf("Case #%d:\n", ++ cas); for (int i = 0; i < n; i ++) { for (int j = 0; j <= 10; j ++) { printf("%d%c", res[i][j], j == 10 ? '\n' : ' '); } } } return 0; }
j Execution of Paladin
题意:http://hs.178.com/201606/259352896164.html
分析:分析毛啊,没玩过炉石的伤不起啊,就这样吧,队友写的
#include<cstdio> #include<queue> #include<functional> #include <iostream> #include <vector> #include <cstring> #include <cmath> #include <algorithm> using namespace std; string s; int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d%d",&n,&m); getchar(); int a=0,b=0,c=0; for(int i=0;i<n;i++) { getline(cin,s); if(s[0]=='M') a++; if(s[0]=='B') b++; if(s[0]=='O') c++; } int cnt=2*(b+c)+2*a*(b+c)+(n-1)*c; if(cnt>=m) printf("Mrghllghghllghg!\n"); else printf("Tell you a joke, the execution of Paladin.\n"); } }
k.Reversed Words
题意:不用说了,一看就知道,直接上代码
#include<bits/stdc++.h> using namespace std; string s; int main(){ int t; scanf("%d",&t);getchar(); while(t--){ getline(cin,s); stack<string> ans; string tmp=""; int len=s.length(); for(int i=len-1;i>=0;i--) if(s[i]!=' ') tmp+=s[i]; else{ ans.push(tmp); tmp=""; } cout<<tmp; while(!ans.empty()){ tmp=ans.top(); ans.pop(); cout<<" "<<tmp; } cout<<endl; } return 0; }
l Password
题意,6种操作,用最少的次数,把第一个数变成第二个数
分析:开始我们都是把注意力集中在了如何把第一个转化为第二个数,只想着记录状态,然后暴力搜索,没有注意到test有1000条,也没有想到把移位操作和加减操作分开,
试了各种千奇百怪的搜索,废话不多说,下面上干货
这个呢就是预处理移位操作,就是原来某位的数字到了其他位置,我位移操作要要走多少步,那么如果用1 2 3 4 5 6表示数字a的6个数的下表,那么总共情况有6!钟,那么数字的位置都确定了,剩下的只要再加上和对应位数字的差就行了,但是还要注意到一点,举个例子 比如状态2 1 3 4 5 6,如果用V[213456表示123456到达这个状态的话,没法表示他对3456这四个位置的修改权利,只有你的下标走到过
L-1,你才对L有修改的权利(思想类黑书钓鱼那道题),这样用6!*6就可以描述所有的移位状态
换位操作,i可以和i-1,i+1进行交换,
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> using namespace std; typedef long long ll; #define rep(i , n) for(int i = 0 ; i < (int)n ; i++) #define rep1(i , x, y) for(int i = (int)x; i<=(int)y; i++) const int inf = 0x3f3f3f3f; int encode(int* a) { int num = 0 ; rep(i, 6) num = num * 10 + a[i]; return num; } void decode(int x,int * a) { for(int i = 5 ; i>= 0 ; i--) { a[i] = x % 10; x/=10; } } const int N = 1e6 + 100; int vis[N][6][6], VIS[N][6]; struct node { int s, p, max_; node(int s = 0,int p = 0, int max_ = 0) :s(s),p(p), max_(max_) {} }; queue<node> q; void Push(int s, int p, int max_,int val) { if(vis[s][p][max_] == -1) { vis[s][p][max_] = val + 1; VIS[s][max_] = min(vis[s][p][max_], VIS[s][max_]); q.push(node(s, p ,max_)); } } void init_bfs() { memset(vis,-1,sizeof(vis)); memset(VIS,inf,sizeof(VIS)); VIS[123456][0] = 0; int a[7]; rep(i, 6) a[i] = i + 1; q.push(node(encode(a), 0, 0)); vis[encode(a)][0][0] = 0; while(!q.empty()) { node u = q.front(); q.pop(); int val = vis[u.s][u.p][u.max_]; if(u.p > 0) { Push(u.s, u.p - 1, u.max_, val); int b[7]; decode(u.s, b); swap(b[u.p], b[u.p - 1]); int nts = encode(b); Push(nts, u.p, u.max_, val); } if(u.p < 5) { Push(u.s, u.p + 1, max(u.max_, u.p + 1), val); int b[7]; decode(u.s, b); swap(b[u.p], b[u.p + 1]); int nts = encode(b); Push(nts, u.p, max(u.max_, u.p + 1), val); } } } int a[7], b[7]; void solve() { int c[6] = {1, 2, 3, 4, 5, 6}; int ans = inf; do { int all = 0, max_ = 0 ; rep(i, 6) { int fro = a[c[i] - 1]; int to_ = b[i]; if(fro != to_) max_ = max(max_, c[i] - 1 ); all += abs(fro - to_); } int exc = inf; for(int i = max_ ; i< 6 ; i++) exc = min(exc, VIS[encode(c)][i]); all += exc; // if(encode(c) == 612345 ){ // cout<<all <<" "<< exc<<" "<< max_ << endl; // } ans = min(ans,all); } while(next_permutation(c, c + 6)); printf("%d\n",ans); } char s[100]; void read(int* a) { scanf("%s",s); rep(i, 6){ a[i] = s[i] - '0'; } } int main() { init_bfs(); int T; scanf("%d",&T); while(T--) { read(a); read(b); solve(); } return 0; }