【链接】;click here~~
【A 题 Nth Largest Value】PKU 3781 http://poj.org/problem?id=3781
【题目大意】输入十个数,输出第三大的数
【 B题 . Equal Sum Partitions】PKU 3782 http://poj.org/problem?id=3782
【题目大意】给你一个整数序列,M(M <= 10000)个元素的序列,定义一个An equal sum partition。满足将序列可以分成几个区间,每个区间的和相等
【解题思路】此题坑点:从前往后寻找,如果找到有跟前面已经找到的区间的和相等的区间,继续寻找,直到序列末尾,输出区间之和,否则在之后的序列里找不到,直接输出当前所有数之和!(也就是求不是单调序列)
理解了如此,此题也就可以直接模拟了,数据水~~
代码:
/* Author :HRW */ #include <bits/stdc++.h> using namespace std; int a[10010]; int solve(int n) { int sum=0; for(int i=0; i<n; i++){ sum+=a[i]; int s2=0; for(int j=1+i; j<n; j++){ s2+=a[j]; if(s2>sum) break; if(s2==sum){ s2=0; if(j==n-1) return sum; } } } return sum; } int main() { int t,m,k,n; cin>>t; while(t--) { cin>>k>>n; for(int i=0; i<n; i++) cin>>a[i]; int ans=solve(n); printf("%d %d\n",k,ans); } return 0; }
【C.Balls】 PKU 3783 http://poj.org/problem?id=3783
【题目大意】 给定 B (B <= 50) 个一样的球,从 M (M <= 1000) 层楼上一个一个往下扔,存在某个楼层 K ,使得低于它的楼层往下扔球,球不会碎,在第 K 层扔下去会碎。求最坏情况下,需要扔几次才能确定这个 K 。【解题思路】此题赛后发现是IOI2004的一篇论文,竟然是道动态规划!,当时没有做出来,很经典了,详细题解可以参考论文原文件:click here~~
【D.Running Median】PKU 3784 http://poj.org/problem?id=3784
【题目大意】:一个长度为M(M <= 9999)的序列,每次奇数位的数读入的时候计算前面整个序列的中位数。
【解题思路】二分 + 树状数组
由于数字是int32范围,所以首先需要将所有数离散到下标,枚举每一个数字读入,将对应位的数字下标插入到树状数组中,每当读到奇数个的时候,利用树状数组的成端求和的性质,如果要找第K大的数,那么就二分一个答案,然后查询树状数组,如果求和大于等于K,表示是一个候选解(因为要保证大于等于K的数最小,才是第K大的数),反复二分,直到找到最小的值V满足sum(V) >= K,时间复杂度O(2 *M * log(M) )
比赛时用的模拟
#include <bits/stdc++.h> using namespace std; int main() { //freopen("1.txt","r",stdin); int P,n,k; int a[10000]; scanf("%d",&P); while(P--) { count = 0; scanf("%d%d",&k,&n); for(int i = 0; i < n; i++) scanf("%d",&a[i]); printf("%d %d\n",k,(n + 1) / 2); for(int i = 1; i <= n; i = i + 2){ count++; sort(a,a+i); if(count % 10!=0) printf("%d ",a[(i-1)/2]); else printf("%d\n",a[(i-1)/2]); } printf("\n"); } return 0; }
二分 + 树状数组
#include<bits/stdc++.h> using namespace std; int a[10005],A[10005],p[10005],n; void add(int i){ while(i<=n){ p[i]+=1; i+=(i&(-i)); } } int sum(int i){ int t=0; while(i){ t+=p[i]; i-=(i&(-i)); } return t; } int find(int k){ int l=1,r=n,res,mid; while(l<=r){ mid=(l+r)/2; if(A[mid]>=k){ r=mid-1; res=mid; } else l=mid+1; } return res; } int main(){ int cas,casn,i,j; scanf("%d",&cas); while(cas--){ scanf("%d %d",&casn,&n); memset(p,0,sizeof(p)); printf("%d %d\n",casn,n/2+1); for(i=1;i<=n;i++){ scanf("%d",&a[i]); A[i]=a[i]; } int tot=n/2+1,cnt=0,pos; sort(A+1,A+n+1); for(i=1;i<=n;i++){ pos=find(a[i]); add(pos); if(i&1){ cnt++; int l=1,r=n,mid,res,k=i/2+1; while(l<=r){ mid=(l+r)/2; if(sum(mid)>=k){ r=mid-1; res=mid; } else l=mid+1; } printf("%d",A[res]); if(cnt%10==0)printf("\n"); else if(cnt!=tot)printf(" "); } } if(tot%10)printf("\n"); } return 0; }
【E.The Next Permutation】 PKU 3785 http://poj.org/problem?id=3785
【题目大意】给定一个可重复元素的排列A[i],求下一个排列。
【解题思路】直接把标题函数用上了,~~>_<
代码:
#include <bits/stdc++.h> using namespace std; int a[10000]; int main() { char str[10000]; int n; int m,i,j; cin>>n; while(n--) { scanf("%d %s",&m,str); int len=strlen(str); if(next_permutation(str,str+len)) printf("%d %s\n",m,str); else printf("%d BIGGEST\n",m); } return 0; }
【题目大意】求长度为n的二进制整数中,相邻两个1的对数有k对(可重复使用)的整数个数,题目描述的很清楚了
【解题思路】: 令长度为n,相邻1的对数为k的数的个数为DP[n][k],其中以0结尾的为DP[n][k][0],以1结尾的为DP[n][k][1],那么 DP[n][k] = DP[n][k][0] + DP[n][k][1];
并且有如下状态转移方程:
1) 长度为n-1的二进制数在末尾加上一个0,相邻1的对数不变,所以有:
DP[n][k][0] = DP[n-1][k][0] + DP[n-1][k][1];
2) 长度为n-1的二进制数在末尾加上一个1,相邻1的对数取决于,第n-1位是0还是1,当第n-1位是1,相邻1的对数+1;当第n-1位是0,相邻1的对数不变,所以有:
DP[n][k][1] = DP[n-1][k][0] + DP[n-1][k-1][1];
并且初始状态下DP[0][0][0] = 1, DP[0][0][1] = 0
代码:
/* Author :HRW dp三维数组,dp[i][j][k]:n,k,(0,1) n位数,值位k,末尾为(0,1) 状态转移方程: dp[i][j][0]+=(dp[i-1][j][0]+dp[i-1][j][1]); dp[i][j][1]+=(dp[i-1][j][0]+dp[i-1][j-1][1]); */ #include <bits/stdc++.h> using namespace std; int dp[1010][1010][2]; int solve(int n,int k) { memset(dp,0,sizeof(dp)); dp[1][0][0]=dp[1][0][1]=1; for(int i=1;i<=n;i++) for(int j=0;j<=k;j++){ dp[i][j][0]+=(dp[i-1][j][0]+dp[i-1][j][1]); dp[i][j][1]+=(dp[i-1][j][0]+dp[i-1][j-1][1]); } return dp[n][k][0]+dp[n][k][1]; } int main() { int t,m,n,k; cin>>t; while(t--) { cin>>m>>n>>k; int ans=solve(n,k); printf("%d %d\n",m,ans); } return 0; }