ไส้อั่วทิพย์ (Sai-ua)

toi18_sausage

อธิบายโจทย์เพิ่มเติม

จากโจทย์ ความอร่อยของไส้อั่วชิ้นที่ ii คือ DiD_{i}

กำหนดให้ ll หมายถึงปลายฝั่งซ้ายของไส้อั่ว และ rr หมายถึงปลายฝั่งขวาของไส้อั่ว

ในการกินไส้อั่วแต่ละครั้งต้องกินส่วนปลายเท่านั้น ไม่ว่าจะเป็นทางซ้ายหรือทางขวา โดยเมื่อกินแต่ละครั้งจะได้ความอร่อยทิพย์ DlDr|D_l - D_r|

โดยความอร่อยในการกินแต่ละครั้งจะมีค่าเท่ากับ ความอร่อยของไส้อั่วชิ้นที่เลือก บวกกับ ความอร่อยทิพย์ครั้งก่อนหน้า สามารถเขียนเป็นสมการได้เป็น

Delnow=Delprev+DchooseDel_{now} = Del_{prev} + D_{choose}

เมื่อ DelDel หมายถึง ความอร่อยทั้งหมด(รวมทั้งความอร่อยทิพย์) ที่สามารถกินได้ เมื่อพิจารณาช่วง ll ถึง rr

แต่ทว่าในการก่อนการกินแต่ละครั้งสามารถเลือกที่จะตัดไส้อั่วก่อนได้ และในบางครั้งการตัดไส้อั่วจะทำให้ความอร่อยรวมทั้งหมดนั้นเพิ่มขึ้น

โดยในการกินทั้งหมดต้องการทำให้ผลรวมความอร่อยทั้งหมดมากที่สุด

กลุ่มชุดสอบที่ไม่มีการตัดไส้อั่ว (12 คะแนน)

เนื่องจากในชุดทดสอบนี้ไม่มีการตัดไส้อั่ว ทำให้จำเป็นต้องคิดเพียงการหา DelnowDel_{now} เท่านั้น

นิยาม dp(l,r)dp(l, r) หมายถึง ความอร่อยมากที่สุดที่สามารถกินได้ในช่วง ll ถึง rr โดยไม่มีการตัดไส้อั่ว

โดย DelnowDel_{now} จะมีค่าเท่ากับ dp(l,r)dp(l, r) และ DelprevDel_{prev} จะมีค่าได้ 2 แบบคือ

  • เลือกฝั่งซ้ายจะทำให้ Delprev=dp(l+1,r)Del_{prev} = dp(l + 1, r)
  • เลือกฝั่งขวาจะทำให้ Delprev=dp(l,r1)Del_{prev} = dp(l, r - 1)

และในการเลือกกินแต่ละครั้งจะเลือกฝั่งที่มีค่ามากที่สุดเพื่อให้ในการกินแต่ละครั้งความอร่อยจะมากที่สุดเท่าที่เป็นไปได้ ดังนั้นสามารถเขียนเป็นสมการได้เป็น

dp(l,r)={Dll=rDlDr+max(dp(l+1,r)+Dl,dp(l,r1)+Dr)l<rdp(l,r) = \begin{cases} D_{l} & \text{; } l = r \\ |D_{l} - D_{r}| + \max(dp(l + 1, r) + D_l, dp(l, r - 1) + D_r) & \text{; } l < r \end{cases}

Time Complexity : O(N2)O(N^{2})

สุดท้ายคำตอบจะอยู่ที่ dp(1,N)dp(1, N) เนื่องจาก dp(l,r)dp(l, r) เป็นคำตอบในช่วง ll ถึง rr เช่นกัน

กลุ่มชุดสอบที่ N1000N\le1\,000 (65 คะแนน)

ในชุดทดสอบกลุ่มนี้สามารถใช้ Matrix Chain Multiplication ในการลองไล่ตัดทุกวิธีได้

นิยาม opt(l,r)opt(l, r) หมายถึง ความอร่อยมากที่สุดที่สามารถกินได้ในช่วง ll ถึง rr โดยมีการตัดกี่ครั้งก็ได้

ในการพิจารณาแต่ละ l,rl, r จะตัดที่จุด kk ทำให้แบ่งไส้อั่วได้ 2 ชิ้นเป็น ll ถึง kk และ k+1k + 1 ถึง rr

opt(l,r)=maxlk<r{dp(l,r)opt(l,k)+opt(k+1,r)opt(l, r) = \max\limits_{l \le k < r}\begin{cases} dp(l, r) \\ opt(l, k) + opt(k + 1, r) \end{cases}

Time Complexity : O(N3)O(N^{3})

สุดท้ายคำตอบจะอยู่ที่ opt(1,N)opt(1, N) เนื่องจาก opt(l,r)opt(l, r) เป็นความอร่อยที่มากที่สุดเมื่อสามารถตัดกี่ครั้งก็ได้ในช่วง ll ถึง rr

ไม่มีเงื่อนไขเพิ่มเติม (100 คะแนน)

ในปัญหาย่อยนี้ต้อง optimize การหา opt(l,r)opt(l, r) ที่เป็นปัญหา Matrix Chain Multiplication ลงให้ใช้เวลาไม่เกิน O(N2)O(N^{2})

ตั้งนิยามใหม่ให้กับ optopt โดย นิยาม opt(r)opt(r) หมายถึง ความอร่อยมากที่สุดที่สามารถกินได้ในช่วง ll ถึง rr โดยมีการตัดกี่ครั้งก็ได้ เมื่อ lrl \le r

opt(r)=max1l<r{dp(1,r)opt(l)+dp(l+1,r)opt(r) = \max\limits_{1 \le l < r}\begin{cases} dp(1, r) \\ opt(l) + dp(l + 1, r) \end{cases}

Time Complexity : O(N2)O(N^{2})

สุดท้ายคำตอบจะอยู่ที่ opt(N)opt(N) เนื่องจาก opt(N)opt(N) เป็นความอร่อยที่มากที่สุดเมื่อสามารถตัดกี่ครั้งก็ได้ในช่วง 11 ถึง NN

#include <bits/stdc++.h>
using namespace std;
const int MxN = 5050;
int n, dp[MxN][MxN], opt[MxN], D[MxN];
int main() {
  cin.tie(nullptr)->ios::sync_with_stdio(false);
  cin >> n;
  for (int i = 1; i <= n; ++i) {
    cin >> D[i];
  }
  for (int i = 1; i <= n; ++i) {
    dp[i][i] = D[i];
  }
  for (int sz = 2; sz <= n; ++sz) {
    for (int l = 1; l + sz - 1 <= n; ++l) {
      int r = l + sz - 1;
      dp[l][r] =
          abs(D[l] - D[r]) + max(dp[l + 1][r] + D[l], dp[l][r - 1] + D[r]);
    }
  }
  for (int r = 1; r <= n; ++r) {
    opt[r] = dp[1][r];
    for (int l = 1; l < r; ++l) {
      opt[r] = max(opt[r], opt[l] + dp[l + 1][r]);
    }
  }
  cout << opt[n];
  return 0;
}