【洛谷P1982】小朋友的數字
阿新 • • 發佈:2018-08-10
就是 提交 負數 else 最大值 const 個數字 狀態 連續
小朋友的數字
沒錯,我又在刷水題
題目鏈接
題目翻譯:
每個小朋友有一個數字,構成一個數字序列a1,a2…an
我們定義“特征值”fi為a1~ai中的最大連續子段和
(不懂請自行百度)
再定義“分數”si為1~i-1中最大的(sj+fj),特殊的,s1=f1,
要求輸出最大的si
DP:
於是我們可以dp求出每個最大連續子段和作為特征值
然後按題意模擬一遍求出每個分數
狀態定義:
dp[i]表示以i為結尾的最大子段和
方程
dp[i]=max(dp[i-1],0)+x; //連著/不連著 前面
f[i]=max(f[i-1],dp[i]);
優化:
空間:
我們發現f[i]、dp[i]都是由f[i-1]、dp[i-1]轉移來的,我們可以考慮將數組降一維 於是空間復雜度就是常數級別的了
時間:
1.1e6的數據快讀是有一定作用的
2.邊讀入邊處理,減少常數
3.由於要用long long,取模運算很慢,可以考慮減少取模次數,當ans>1e17時再取模
#include<cstdio> using namespace std; #define int long long #define N 1000010 #define INF 0x3f3f3f3f const int ch_top=4e7+3; char ch[ch_top],*now_r=ch-1,*now_w=ch-1; inline int read(){ //快讀 int f=1; while(*++now_r<‘0‘) if(*now_r==‘-‘) f=-1; register int x=*now_r-‘0‘; while(*++now_r>=‘0‘)x=(x<<3)+(x<<1)+*now_r-‘0‘; return x*f; } inline void write(int x){ //並沒用什麽卵用的快寫 if(x<0){*++now_w=‘-‘,x=-x;} static char st[20];static int top; while(st[++top]=‘0‘+x%10,x/=10); while(*++now_w=st[top],--top); *++now_w=‘\n‘; } int n,p,dp,ans1,ans; bool flag; #undef int int main() #define int long long { fread(ch,1,ch_top,stdin);//快讀 n=read(); p=read(); int x,f; dp=f=read(); ans1=f;ans=f*2; //特殊處理第一個數 /*ans1為第一個小朋友的分數, ans為其他小朋友的分數的最大值*/ for(int i=2;i<n;i++){ x=read(); dp=(dp>0?dp:0)+x; if(dp>f) f=dp; if(f>0) ans=ans+f; if(ans>1e17) { ans%=p,flag=1; } /*當ans為負時,一定不會是兩個以上的 負數之和,不會爆ll,若取過模,ans一定大於 ans1,用一個flag記錄*/ } if(flag)write(ans%p); else{ if(ans1>ans) ans=ans1; write(ans%p); } fwrite(ch,1,now_w-ch,stdout); //快寫 return 0; }
開O2 32ms ,大概是非打表提交的最優解
【洛谷P1982】小朋友的數字