codeforces round 171 div2

http://codeforces.com/contest/279/problem/A

A:一个螺旋线,自己画画图就知道规律,给你一个点,问这个点在该螺旋线上转了几个弯。

思路:我是模拟做的,因为数据量不大,从内往外暴力,看点是否在当前的线段上即可。

#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
int dir[4][2]={1,0,0,1,-1,0,0,-1};
int main()
{
    int x,y;
    scanf("%d%d",&x,&y);
    int sx=0,sy=0,ans=0,d=0,len=1,tx=0,ty=0;
    while(1)
    {
        int num=2;
        while(num--)
        {
            //printf("%d %d\n",tx,ty);
            d%=4;
            sx=tx,sy=ty;
            tx=sx+len*dir[d][0],ty=sy+len*dir[d][1];
            if(d%2)
            {
                if(x==sx&&(y-sy)*(y-ty)<=0)
                {
                    printf("%d\n",ans);
                    return 0;
                }
            }
            else
            {
                if(y==sy&&((x-sx)*(x-tx))<=0)
                {
                    printf("%d\n",ans);
                    return 0;
                }
            }
            ans++;
            d++;
        }
        len++;

    }
    return 0;
}


http://codeforces.com/contest/279/problem/B

B:给你N个数,求最长的连续区间,是该区间的数字之和不大于T。

思路:乍一看觉得二分即可,其实存在更好的方法。我们从左往右扫描,设l为当前的区间的左端点,初始为1,设当前遍历到i,则若sum[l,i]>t,则我将l++直到sum[l,i]<=t,为止,此时i-l+1为以i结尾的区间的最大值,设为tmp,我们求遍历过程中tmp的最大值即可。时间复杂度为O(n)。

#include <iostream>
#include <string.h>
#include <stdio.h>
#define maxn 100010
using namespace std;
int a[maxn];
int max(int a,int b)
{
     return a>b?a:b;
}
int main()
{
    //freopen("dd.txt","r",stdin);
    int n,t,i;
    scanf("%d%d",&n,&t);
    for(int i=n;i>=1;i--)
    scanf("%d",&a[i]);
    int l=0,sum=0,ans=0;
    for(i=1;i<=n;i++)
    {
        sum+=a[i];
        if(sum<=t)
        ans=max(ans,i-l);
        else
        {
            while(sum>t)
            {
                sum-=a[++l];
            }
         ans=max(ans,i-l);
        }
    }
    printf("%d\n",ans);
    return 0;
}

http://codeforces.com/contest/279/problem/C

C:给你n个数a[1]~a[n],询问一段区间[l,r]是否满足前面一段时非递减的,后面一段是非递增的。(前后两段可为空)

思路:设mi[i]和ma[i],分别表示以a[i]结尾,非递减子串的左端和非递增子串的左端。这个可以在O(N)复杂度内求解。询问时,若mi[r]<=l或ma[i]<=l,则说明区间[l,r]为非递减串或非递增串,输出YES,否则设po=mi[r],表示区间[po,r]为非递增串,若ma[po]<=l,则说明区间[l,po]为非递减,[po,r]为非递增的,输出YES,否则输出NO。

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define maxn 100010
using namespace std;
int a[maxn];
int ma[maxn],mi[maxn];
int main()
{
    //freopen("dd.txt","r",stdin);
    int n,i,q;
    scanf("%d%d",&n,&q);
    for(i=1;i<=n;i++)
    scanf("%d",&a[i]);
    ma[1]=mi[1]=1;
    for(i=2;i<=n;i++)
    {
        if(a[i]>a[i-1])
        {
            ma[i]=ma[i-1];
            mi[i]=i;
        }
        else if(a[i]<a[i-1])
        {
            ma[i]=i;
            mi[i]=mi[i-1];
        }
        else
        {
            ma[i]=ma[i-1];
            mi[i]=mi[i-1];
        }
    }
    while(q--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        if(ma[r]<=l||mi[r]<=l)
        {
            printf("Yes\n");
        }
        else
        {
            int po=mi[r];
            if(ma[po]<=l)
            printf("Yes\n");
            else
            printf("No\n");
        }
    }
    return 0;
}

http://codeforces.com/contest/279/problem/D

D:看了becauseofyou大神的结题报告才做出来的,代码基本参考了becauseofyou大神的,第一个状态压缩DP。

把状态用二进制表示,设状态x的最高位的1是第i位,表示a[i]要用前面的数组合而成,第i位之前的1表示当前状态已经保存了a[j](0<=j<i且x的第j位为1),0表示当前状态不确定是否保存了a[j](0<=j<i且x的第j位为0).设dp[x]为在x状态下所要的最小变量数。则我们要求的即为dp[1<<(n-1)]。因为每个数都是一个一个求出来的,所以再求状态x时,它上一个状态一定包含了前一个要求的数(即a[i-1]),且如果a[i]==a[j]+a[k](0<=j,k<i),表示a[i]是由a[j]和a[k]转移来的,所以上一个状态一定以保存了a[j]和a[k],所以状态转移方程就出来了。

还有一个要注意的一点,设num为状态x用二进制表示中1的个数,则dp[x]至少为num,这个由状态x的意义即可理解。

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define inf 2100000000
using namespace std;
int a[25],pow[23];
int dp[1<<23],n;
void init()
{
    memset(dp,-1,sizeof(dp));
    pow[0]=1;
    for(int i=1;i<23;i++)
    pow[i]=2*pow[i-1];
}
int min(int a,int b)
{
    return a<b?a:b;
}
int max(int a,int b)
{
    return a>b?a:b;
}
int dfs(int x)
{
    if(dp[x]!=-1)
    return dp[x];
    int num=__builtin_popcount(x);
    int ans=inf,tmp;
    for(int i=n-1;i>=0;i--)
    {
        if(x&pow[i])
        {
            int tt=(x^pow[i])|pow[i-1];
            for(int j=i-1;j>=0;j--)
            {
                for(int k=j;k>=0;k--)
                {
                    if(a[i]==a[j]+a[k])
                    {
                        tmp=dfs(tt|pow[j]|pow[k]);
                        if(tmp!=inf)
                        ans=min(ans,max(tmp,num));
                    }
                }
            }
            break;
        }
    }
    dp[x]=ans;
    return ans;
}
int main()
{
    //freopen("dd.txt","r",stdin);
    int i;
    init();
    scanf("%d",&n);
    for(i=0;i<n;i++)
    scanf("%d",&a[i]);
    dp[1]=1;
    int ans=dfs(pow[n-1]);
    if(ans==inf)
    ans=-1;
    printf("%d\n",ans);
    return 0;
}


http://codeforces.com/contest/279/problem/E

E:给你一个用二进制表示的数a[1]~a[n],求用最少的 2^k和-2^k形式的数,它们的和等于所给的数。

思路:贪心也可以,不过我是DP做的,还是挺简单的。一个简单的结论就是对于所用的数的k各不相同。

设dp[i]表示最后i位数所需要的最小值,我们从后往前遍历。

当a[i]=1,则我们可以直接加上2^i,即dp[i]=dp[i+1],或者我们可以用2^(i+1)-2^j(0<=j<i)使得第i位为1,但是这样还没完,我们还要加上dp[n-j+1],并且对于[i,n-j]中的0,我们还要用-2^x(i<x<=n-j且a[x]==0),我们要使得所取得数最小,就要使得dp[n-j+1]+2+num(num为区间[i,n-j]中0的个数)最小,我们设这个值对于不同的位置x为w[x],这似乎需要O(n^2)的复杂度,其实不然,其实具观察可发现,如果对于位置po最小,事实上,若a[i]=1,num不变,则还是w[po]最小。

若a[i]=0,则num++,w[po]++,这时候只有w[i]可能小于w[po],我们比较一下更新即可。最后复杂度可以达到O(n)。

(表达能力屎一样啊,说的一点都不明白,还是看代码吧)

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
char str[1000010];
int dp[1000010];
int min(int a,int b)
  {
    return a>b?b:a;
    }
int main()
{
   // freopen("dd.txt","r",stdin);
  int i,tmp=0;
  scanf("%s",str+1);
  int n=strlen(str+1);
  dp[n]=str[n]-'0';
  for(i=n-1;i>=1;i--)
    {
      if(str[i]=='1')
        dp[i]=min(dp[i+1]+1,tmp+2);
      else
        {
          tmp++;
          dp[i]=dp[i+1];
          tmp=min(tmp,dp[i]);
          }
      }
  printf("%d\n",dp[1]);
  return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值