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 100010using 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 100010using 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 2100000000using 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;}