SRM 09

A

  这道题就是要求删掉最右边若干本数之后,使得最终撕掉的书页最少。

  先按照P值从小到大排个序,然后先建立一个数组s[i],表示执行第i页的指令最远能撕到第几页(排序好之后的),由于P[i]是有序的,因此可以用二分查找。再用ans[i]表示若将i页之后的书页全部删去,最终会撕掉几页书,很显然ans[i]=ans[s[i]-1]+i-s[i],然后就不停地更新答案就行了。

代码:

#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<iostream>
using namespace std;

int p[1000],a[1000],s[1000],i,j,n,ans[1000],Ans=0x7fffffff;

void qs(int l,int r)
{
    int i=l,j=r,m=p[(l+r)>>1],t;
    while (i<=j)
    {
        while (p[i]<m) i++;
        while (p[j]>m) j--;
        if (i<=j)
        {
            t=p[i];p[i]=p[j];p[j]=t;
            t=a[i];a[i]=a[j];a[j]=t;
            i++;j--;
        }
    }
    if (i<r) qs(i,r);if (l<j) qs(l,j);
}

int Search(int k)
{
    int m,l=1,r=n;
    while (l<=r)
    {
        m=(l+r)>>1;
        if (p[m]<k) l=m+1; else r=m-1; 
    }
    return l;
}

main()
{
    scanf("%d",&n);
    for (i=1; i<=n; i++) scanf("%d%d",&p[i],&a[i]);
    qs(1,n);
    for (i=1; i<=n; i++)
    {
        s[i]=Search(p[i]-a[i]);
        ans[i]=ans[s[i]-1]+i-s[i];
        Ans=min(Ans,n-i+ans[i]);
    }
    printf("%d
",Ans);
}
A

题目

B

  给出两个数,一个是两数之和a,一个是两数异或之后的值b。

  由于异或是将两个数进行不进位的加法,因此设c=a-b为两数因为没有进位而造成的差,由于进位不可能在第一位,因此先将c右移一格。

  接着将b与c逐位转化为二进制,将每一位进行比较。若b[i]==1,c[i]==0,则表示这一位没有进位并且和为1,这样在这一位的情况有{1+0}{0+1},答案就*2;若b[i]==0,c[i]==1,则表示这一位有进位,所以在这一位只有{1+1}1种情况;若b[i]==0,c[i]==0则表示没有进位且和为0,只有{0+0}1种情况;若b[i]==1,c[i]==1,则不存在这样的情况,答案*0。

  最后特判一下若一开始a==b则ans-=2,因为题目要求是正整数,而这种情况下存在0+x和x+0的情况。

代码:

#include<cmath>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define INF 0x7fffffff
#define ll long long
using namespace std;

ll i,a,b,z[42],x[42],y[42],c,ans=1,l,r;

main()
{
    scanf("%lld%lld",&a,&b);
    c=(a-b)>>1;l=a;r=b;
    for (i=1; i<=41; i++) x[i]=c&1,c>>=1;
    for (i=1; i<=41; i++) y[i]=b&1,b>>=1;
    for (i=1; i<=41; i++)
    {
        if (x[i]==0 && y[i]==1) z[i]=2;
        if (x[i]==1 && y[i]==0) z[i]=1;
        if (x[i]==1 && y[i]==1) z[i]=0;
        if (x[i]==0 && y[i]==0) z[i]=1;
        ans*=z[i];
    }
    if (l==r) ans-=2;
     printf("%lld
",ans);
     return 0;
}
B

题目

C

  其实C的DP并不难写,考试的时候智障根本没想DP。

  设f[i][j]为撕掉[i,j]范围内的书最少要几步。于是先预处理,撕掉单一页的书只需要一步,因为单个字符就是一个回文串。然后就可以DP了。

  第一重枚举右端点j,第二重枚举左端点i,若a[i]==a[j]那当前的最小方案数就是f[i+1][j-1](这里要注意,当i+1==j时,f[i][j]=1;否则=f[i+1][j-1]);若不相同则赋值INF。接着不管f[i][j]是f[i+1][j-1]还是INF都交给下一重循环解决。设k由i枚举到j-1,在这个过程中不断更新f[i][j]的答案:f[i][j]=min(f[i][j],f[i][k]+f[k+1][j])。最终答案为f[1][n]。

代码:

#include<cmath>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define INF 0x7fffffff
using namespace std;

int f[1000][1000],a[1000],i,j,k,n;

main()
{
    scanf("%d",&n);
    for (i=1; i<=n; i++) scanf("%d",&a[i]);
    for (i=1; i<=n; i++) f[i][i]=1;
    for (j=2; j<=n; j++)
    {
        for (i=j-1; i; i--)
        {
            if (a[i]==a[j])
            {
                if (i+1==j) f[i][j]=1;
                else f[i][j]=f[i+1][j-1];
            }
            else f[i][j]=INF;
            for (k=i; k<j; k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
        }
    }
    printf("%d
",f[1][n]);
    return 0;
}
C

题目

原文地址:https://www.cnblogs.com/HAdolf-HQY/p/7288956.html