刷題總結——array(ssoj)
題目:
題目描述
給定 2 個正整數序列 A1, A2,序列長度分別為 L1, L2。
你可以進行以下的一次操作:
1. 選擇兩個數 K1,K2(1≤K1≤L1, 1≤K2≤L2);
2. 移去 A1 中最後 K1 個數,得到這 K1 個數的和 S1,L1 對應減少 K1;
3. 移去 A2 中最後 K2 個數,得到這 K2 個數的和 S2,L2 對應減少 K2;
此次操作的費用為:(S1-K1) * (S2-K2)。
進行以上操作直至兩個序列都為空,求最小的費用總和。
註意:序列為空當且僅當兩個序列同時為空。
輸入格式
第一行是兩個正整數 L1和 L2,表示 A1 與 A2 的長度。
第二行 L1 個整數,表示序列 A1[1..L1]。
第三行 L2 個整數,表示序列 A2[1..L2]。
輸出格式
輸出一個整數,表示最小費用。
樣例數據 1
輸入 [復制]
3 2
1 2 3
1 2
輸出
2
備註
【樣例說明】
第一次選取 K1=1,K2=1。費用為 (3-1)*(2-1) = 2。
第二次選取 K1=2,K2=1。費用為 (1+2-2)*(1-1) = 0。
所以,總費用為 2。
【數據範圍】
對 20% 的輸入數據:1≤L1,L2≤20
對 40% 的輸入數據:1≤L1≤400;1≤L2≤150
對 100% 的輸入數據:1≤L1,L2,A1[1..L1],A2[1..L2]≤5,000
題解:
很好的一道dp題·····
首先容易想到,為了消除每次sum-k中k帶來的影響,我們可以將所有元素-1,這樣每次計算的時候直接sum相乘即可····
然後考慮消除的策略···
打個比方l1=l2=4···我們如果要消除a1[l1]到a1[2]和a2[l2]到a2[3]這兩段區間的數的話···最好的策略肯定不是直接一次性消除···而是先消除a1[l1]和a2[l2]這兩個數,再消除a1[3]到a1[2]和a2[3]這兩段····因此不難發現··每次消除的話a1和a2的區間長度有一個一定為1!
所以我們可以將區間消除轉化為要麽消除兩段末端a1[x],a2[y]中其中一個··要麽同時消除兩個末端··且此時對答案的貢獻為a1[x]*a2[y];
得出dp方程
f[i][j]=min(f[i][j+1],f[i+1][j],f[i+1][j+1])+a1[i+1]*a2[j+1]
其中f[i][j]表示兩段分別剩余i,j個數時的最少費用
代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=5001; const int inf=0x3f3f3f3f; inline int R() { char c;int f=0,i=1; for(c=getchar();(c<‘0‘||c>‘9‘)&&c!=‘-‘;c=getchar()); if(c==‘-‘) c=getchar(),i=-1; for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f*i; } int l1,l2,a1[N],a2[N]; int f[N][N]; int main() { //freopen("a.in","r",stdin); l1=R(),l2=R(); memset(f,inf,sizeof(f)); for(int i=1;i<=l1;i++) a1[i]=R(),a1[i]--; for(int i=1;i<=l2;i++) a2[i]=R(),a2[i]--; f[l1-1][l2-1]=a1[l1]*a2[l2]; for(int i=l1-1;i>=0;i--) for(int j=l2-1;j>=0;j--) { if(i==l1-1&&j==l2-1) continue; f[i][j]=min(f[i][j+1],min(f[i+1][j+1],f[i+1][j]))+a1[i+1]*a2[j+1]; } cout<<f[0][0]<<endl; return 0; }
刷題總結——array(ssoj)