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;
}