2016年山东省acm比赛题解(差l和h)
Posted 罚时自动机
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2016年山东省acm比赛题解(差l和h)相关的知识,希望对你有一定的参考价值。
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以上是关于2016年山东省acm比赛题解(差l和h)的主要内容,如果未能解决你的问题,请参考以下文章