【组队赛#6 Greater New York Regional 2009】解题总结

【链接】;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;
}

F.Adjacent Bit Counts】PKU 3786 http://poj.org/problem?id=3786

【题目大意】求长度为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;
}

你可能感兴趣的:(ACM,比赛题)