online judge:ycjsoi樂樂現在掌管乙個大公司,辦公樓共有n層。為了增加員工的身體素質,他決定在每層樓都建立乙個活動室,活動室分桌球和排球兩種。label:dp,思維題,預處理,滾動優化
已知每層樓喜歡桌球和排球的人數。
每個人的行走樓層數是他去自己喜歡的運動室的樓層數。
請你幫樂樂算算,每層樓應該建桌球還是排球,使得所有人行走樓層數總和最小。
第一行乙個整數n,表示樓層數量。
接下來n行 ,每行兩個整數a和b,表示喜歡桌球和排球的人數。
輸出乙個整數,表示所有人行走樓層總和的最小值。
input#1
2
10 5
4 3
output#1
9
input#2
15
68 42
1 35
25 70
59 79
65 63
46 6
28 82
92 62
43 96
37 28
5 92
54 3
83 93
17 22
96 19
output#2
506
hint
第一層建桌球室,第二層建排球室。行走樓層為5+4=9
對於30%的資料,n的範圍\([2,20]\);;
對於80%的資料,n的範圍\([2,500]\);
對於100%的資料,n的範圍\([2,4000]\),每層樓喜歡桌球和排球的人數範圍\([1,10^5]\)。
看資料範圍應該是個dp。
ⅰ狀態定義
一開始看題,不難定義出這樣乙個狀態\(dp[co=0/1][i][j]\),意義是,已經決策完了前i層建什麼,且\([j+1,i]\)這些層建的都是\(co\),也就是\(j\)這一層建的是\(co\)^\(1\),此時的最小代價。
所以改一下意義,它表示的是[1,j]這些層所有人的代價+[j+1,i]這些層中喜歡co的人的代價
。
ⅱ轉移初始化如下
memset(dp,-1,sizeof(dp));//相當於賦乙個極大值
dp[0][1][0]=dp[1][1][0]=0;
轉移採用填表方式,更新最小值。
for(i=1;i主要看到\(x!=y\)時的情況,我們此時在中間\([j+1,i]\)建了\(x\),在兩端\(j,i+1\)建了\(y(!x)\),那個\(calc(co,l,r)\)函式,求的就是\([l,r]\)這些層中喜歡\(co\)的人,分別去到\(l-1,r+1\)的最小代價。
很明顯以中點為邊界,左半邊的去\(l-1\),右半邊的去\(r+1\)最優吧。
最low的求法應該就是下面這種\(o(n)\)的:
雖然非常暴力,但一定要注意下面幾處特判。
inline ll calc(int co,int l,int r)
if(r==n)
for(i=l;i<=mid;++i)sum+=(i-l+1)*a[co][i];//左半邊的去l-1層
for(i=mid+1;i<=r;++i)sum+=(r-i+1)*a[co][i];//右半邊的去r+1層
return sum;
}
最後的統計答案如下。別忘了還要加上最後一段代價。
類似的,去右端的情況,預處理乙個字尾值即可,下面不再贅述。for(x=0;x<=1;++x)for(j=1;j這樣,大致的演算法流程就結束了。但是也許在上面**中你就發現了,空間、時間對於100%資料都承受不了(時間複雜度為\(o(4\cdot n^3)\)),只能過掉80%資料。
。ⅲ優化·空間
空間的話,主要是原來的\(dp[2][n][n]\)陣列記憶體好像有點玄,所以求穩優化一波。由於每個\(i\)只用到上一層狀態,所以直接滾動就好了,變成dp[2][2][n]
ⅲ優化·時間
時間呢,感覺那個
calc(co,l,r)
函式非常可優化的樣子。在草稿紙上列了一下,發現可以利用差分思想,\(o(n)\)預處理字首/字尾,然後\(o(1)\)計算出結果。舉個例子:對於求\([l,r]\)到它\(l-1\)的代價
比如\(l=4,r=7\)的情況:
列式為\(cost=a[4]*1+a[5]*2+a[6]*3+a[7]*4\)。
我們預處理乙個字首值:
isum[i]=
\((a[1]*1+a[2]*2+a[3]*3+..a[i]*i)\);然後再處理乙個普通的字首和:
s[i]=(a[1]+a[2]+a[3]+..a[i])
;那麼上式\(cost=isum[7]-(s[7]-s[3])*3\)。
推廣一下可以得到:
inline ll getl(int co,int l,int r)
綜上,時間複雜度為\(o(n^2)\)。
完整**如下:
#includeusing namespace std;
typedef long long ll;
const int n=4002;
ll ans=-1;
int a[2][n],n;
inline int read()
inline void do(ll &x,ll y)
ll s[2][n],isum[2][2][n];//isum:sum of i*val
void pre()
} for(int i=n;i>=1;i--) }}
inline ll getl(int co,int l,int r)
inline ll getr(int co,int l,int r)
inline ll calc(int co,int l,int r)
ll dp[2][2][n];
namespace p100{
void solve(){
pre();
memset(dp,-1,sizeof(dp));
register int i,j,x,y,g=0;
memset(dp[g],-1,sizeof(dp[g]));
dp[0][g][0]=dp[1][g][0]=0;
for(i=1;iupd:由於上面**比較醜,所以在部落格檔案裡還放了幾位學長的解析。