algorithm 题集四 (16.06.10)
继2016.05.24续:
codeforces 651B. Beautiful Paintings-简单
大意:给出一个序列,求解其任意排列中满足ai + 1 > ai 的元素个数最大和。 分析:理想情况下,无重复元素的0从小到大的排列,满足条件的元素个数最多,是n-1. 非理想情况下还有重复元素,只要不断提取出重复的这些元素归到另一集合再这样讨论即可。
#include #include #include using namespace std;int s1[1009],s2[1009];int main(){ //freopen("cin.txt","r",stdin); int n; while(cin>>n){ for(int i=0;izoj 2343 Robbers-简单
大意:n个抢到分配赃物,要将总量是M的金币分配完全,每人得到ki,且误差|Xi/Y - Ki/M|的累计最小。 分析:误差式可以化成(XiM-KiY)/(YM),于是对于每一个金币,我给XiM最大的人即可。使用优先队列,每一模拟
#include #include #include #include #include using namespace std;typedef long long LL;LL n,m,y,xx;struct node{ LL x,k,dex; void show(){ cout<,cmp> que; for(int i=0;i0){ node temp=que-(); que.pop(); temp.x-=y; temp.k++; que.push(temp); m--; } int top=0; while(!que.empty()){ p[top++]=que-(); que.pop(); } sort(p,p+top,cmp2); for(int i=0;i或者,先按比例分配,然后对于x[i]/Y - k[i]/M,谁最大我分给谁。注意,不是绝对值。这种解法更容易编码。
codeforces 651A. Joysticks-简单贪心
大意:两个操作杆,一个充电器,每一分钟可以选择一个操作杆充电,增加 1 percent,没有充电的减少2 percent。原来两者的电量是a,b。求解能维持两者电量都大于0的分钟数。 分析:木桶原理,保证两者都要大于0,那么谁少就给谁充电。
#include #include #include using namespace std;int main(){ int a1,a2; while(cin>>a1>>a2){ int sec=0; if(a1>a2) swap(a1,a2); while(a1>=1 && a2>=2){ a1++; a2-=2; if(a1>a2) swap(a1,a2); sec++; } printf("%d\n",sec); } return 0;}
acdream 1220 Hyperhuffman-哈夫曼编码简单题
大意:给出每一个字符出现的次数,问哈弗曼编码的带权路径长度。 分析:关于哈弗曼编码,将每一个字符出现的次数作为树的结点的值,然后每一次取集合中最小的两个值合并,持续下去,直到剩下一个值,作为根节点。 本题自然是模拟中间两两合并的过程即可。要求输出length of text, bits. 那么所有的结点的值之和即是所求。
#include #include #include #include using namespace std;typedef long long LL;struct cmp{ bool operator ()(const LL a,const LL b){ return a>b; }};int main(){ int n; LL a; while(cin>>n){ priority_queue,cmp> que; for(int i=0;icodeforces 650B. Image Preview-二分 好题
大意:n张相片,从第一张相片开始,可以向左或向右滑动查看不同的相片,手机的方向是vertical,相片可能vertical,也可能horizontal,查看过的相片不再查看,不过要计算滑动的时间。在总时间T秒内最多能查看多少张相片? 分析:感觉变化好多啊,翻页花费的时间,调整方向花费的时间,看相片花费的时间。仿佛没有规律。 暴力是个好东西,专门解决没有规律的问题。不过暴力也要有技巧的暴力,比如,二进制枚举,二分查找等。 分析发现,最终的结果也就是向左、向右查看了多少相片。所以我们可以一个方向上直接枚举,另一个方向上二分加速。
工具好不好用,能发挥多大的威力完全在于使用它的人啊。此题再次说明了这个道理。一般来讲,寻找极限值使用三分查找的,但是三分往往针对浮点数有效。此题是整数问题,我见识到了用二分解决整数极值问题。
#include #include #include using namespace std;const int N=5e5+10;char str[N];int sum[N];int n,a,b,t;bool check(int w){ int ans=0,temp=0; for(int i=1;i<=w;i++){ ans=sum[i]+sum[n]-sum[n-(w-i)]; // 前缀和能求出任意段值 temp=min(a*(i-1)*2+(w-i)*a,a*(w-i)*2+a*(i-1)); //右右左、左左右 ans=ans+temp; if(ans<=t) return 1; } return 0;}int main(){ //freopen("cin.txt","r",stdin); while(~scanf("%d%d%d%d",&n,&a,&b,&t)){ scanf("%s",str+1); for(int i=1;i<=n;i++){ sum[i]=sum[i-1]+1; if(str[i]=='w') sum[i]+=b; } int ans=0,l=1,r=n,mid; // binary search for number of photo(answer)! while(l<=r){ mid=(l+r)>>1; if(check(mid)){ ans=ans>mid?ans:mid; l=mid+1; } else r=mid-1; } printf("%d\n",ans); } return 0;}
codeforces 652B. z-sort -简单
大意:给出n个数字,求解其满足这样的排列。 ai ≥ ai - 1 for all even i, ai ≤ ai - 1 for all odd i > 1. 分析:先把前n/2个数字放在偶数位,然后在奇数位上放入剩下的数字。
#include #include #include using namespace std;int a[1009],b[1009];int cmp(int t1,int t2){ return t1>t2;}int main(){ //freopen("cin.txt","r",stdin); int n; while(cin>>n){ for(int i=0;i>1,top=0; for(int i=0;iACdream - 1195 Sudoku Checker-递归 or math
大意:检测一个矩阵是否满足数独的特征。 Each row contains each number from 1 to N2, once each. Each column contains each number from 1 to N2, once each. Divide the N2×N2 matrix into N2 non-overlapping N×N sub-matrices. Each sub-matrix contains each number from 1 to N2, once each. 分析: 用一维数组做,易出错啊,交了四次。直接计算每一个起点和起点对应的小矩阵里的每一个元素的位置。即用纯数学思维,找每一个元素的位置。
#include #include #include using namespace std;int g[1600];bool tag[1050];bool check(int s,int N,int n){ memset(tag,0,sizeof(tag)); for(int i=0;i>t; while(t--){ scanf("%d",&n); int N=n*n; for(int i=0;i用二维数组做,归纳的思想,不易出错。将每一个n*n的小部分看做n^4的一个格子。即:
那么我们令大格子的迭代器x: i, y: j 每一个小格子n*n部分的左上角位置就是(i-1)n+1, (j-1)n+1 (设数组各维的起点是1)
#include #include #include using namespace std;int g[40][40];bool vis[1009];int main(){ int t,n,ca=1; cin>>t; while(t--){ scanf("%d",&n); int N=n*n; for(int i=1;i<=N;i++){ for(int j=1;j<=N;j++){ scanf("%d",&g[i][j]); } } bool ok=1; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ int start_x=(i-1)*n+1,x_step=n; int start_y=(j-1)*n+1,y_step=n; memset(vis,0,sizeof(vis)); for(int ii=start_x;x_step>0;ii++,x_step--){ y_step=n; for(int jj=start_y;y_step>0;jj++,y_step--){ vis[g[ii][jj]]=1; } } for(int ii=1;ii<=N;ii++){ if(vis[ii]==0) { ok=0; break; } } } } for(int i=1;i<=N;i++){ memset(vis,0,sizeof(vis)); for(int j=1;j<=N;j++){ vis[g[i][j]]=1; } for(int j=1;j<=N;j++) if(vis[j]==0) ok=0; } for(int j=1;j<=N;j++){ memset(vis,0,sizeof(vis)); for(int i=1;i<=N;i++){ vis[g[i][j]]=1; } for(int i=1;i<=N;i++) if(vis[i]==0) ok=0; } if(ok==0) printf("Case #%d: No\n",ca++); else printf("Case #%d: Yes\n",ca++); } return 0;}
ACdream - 1187 Rational Number Tree -tree 规律
大意:有一颗分子树是这样的:
1/1 ______|______ | | 1/2 2/1 ___|___ ___|___ | | | |1/3 3/2 2/3 3/1...
left and right childs of node p/q are p/(p+q) and (p+q)/q, respectively 现在有两种问法,一种是给出一个数字,作为结点的位置,求解结点的分子和分母。另一种是给出结点的分子和分母,求解它在树中的位置(层次遍历)。 分析:解题思路一定和路径相关。这听起来是一句废话,但是确实能起很大作用。通过观察发现,
1 ______|______ | | 2 3 ___|___ ___|___ | | | | 4 5 6 7
位置是偶数的相对父节点而言是在左边,且是父节点位置的2倍;是奇数的相对父节点而言是在右边,位置比左兄弟大1。 分支规律:
a/b ______|______ | | a/(a+b) (a+b)/b
那么我们对位置dex分解,用一个栈存储分解信息。
char sta[1000]; int top=0; void solve(dex){ if(dex is odd) { sta.push('B'); // 右边 dex--; } else { sta.push('A'); // 左边 dex>>=1; }}
再根据路径信息寻找分子。
work(a,b,key){ if(key=='A') return(a,a+b); else return (b,b-a);
由a/b寻找位置dex,观察容易发现,只有根结点分子和分母是相等的。再结合这颗树的分支特征,我们不断向上直达根部,存储路径信息,最后由路径信息计算位置.
while(a!=b){ if(a>b){ LL t=b; b=a; a=a-t; sta.push('B'); } else { b=b-a; sta.push('A'); }}
最后,信息先进后出,计算位置.
while(top>0){ if(sta[top-1]=='A') ans<<=1; else ans++; top--;
code:
#include #include using namespace std;typedef unsigned long long LL;struct node{ LL a,b; node(LL t1,LL t2){ a=t1; b=t2; } void show(){ cout<<" ("<>t; while(t--){ scanf("%d",&key); LL dex,a,b; if(key==1) { scanf("%llu",&dex); node p(1,1); top=0; while(dex>1){ if(dex&1) { dex--; sta[top++]='B'; } else { dex>>=1; sta[top++]='A'; } } while(top>0){ p=work(p,sta[--top]); //p.show(); } printf("Case #%d: %llu %llu\n",ca++,p.a,p.b); } else { scanf("%llu %llu",&a,&b); top=0; LL ans=1; while(a!=b){ if(a>b){ LL t=b; b=a; a=a-t; sta[top++]='B'; } else { b=b-a; sta[top++]='A'; } } while(top>0){ if(sta[top-1]=='A') ans=ans<<1; else ans++; top--; } printf("Case #%d: %llu\n",ca++,ans); } } return 0;}
codeforces 659A. Round House - math
注意b可能小于0,且绝对值远大于n。
#include #include #include using namespace std;int main(){ int n,a,b; while(~scanf("%d%d%d",&n,&a,&b)){ /*int bb=abs(b); while(bb>0){ if(b>0){ a=a+1; if(a==n+1) a=1; } if(b<0){ a=a-1; if(a==0) a=n; } bb--; } printf("%d\n",a); */ int ans=((a-1+n+b)%n+n)%n; printf("%d\n",ans+1); } return 0;}
codeforces 659C. Tanya and Toys - hash STL
大意:给出n个数字(1——1e9),求出剩下尽可能多的数字(在1——1e9内)使得他们的和不大于m。 分析:hash标记出现过的数字,再由小到大遍历没有出现的数字,和不大于m即可。
#include #include #include #include
暂时没有评论,来抢沙发吧~