題目鏈結:
題目大意:有一圈整數(一共n個),按順序將其分為m個部分,各部分內的數字相加,相加所得的m個結果對10取模後再相乘,最終得到乙個數k。要求是使你所得的k最大或者最小。
題目分析:
1.首先對於環狀的題目首先想到的就是將環狀的轉化為線性的,這也將大大簡化我們解題的過程。我們可以將環從n和1中間斷開,將兩個數段拼到一起,效果就是:
a1,a2,……,an-1,an,a1,a2……,an-1
最後不要an的原因是所有的結果都會在長度為n的區間完成,而最遠的點就是an->an-1,所以不需要an。
2.而面對要分成m個部分的這種題,前x-1個部分的情況不影響第x組的結果,所以很容易想到用動態規劃的方法來儲存前i個元素分成j部分的最大最小值。因為我們把環變成了乙個數段,所以可以直接列舉起點和終點。
3.狀態轉移方程如下:
dp[l][r][j][0]=min(dp[l][r][j][0],dp[l][k][j-1][0] * (pre[r]-pre[k]));
dp[l][r][j][1]=max(dp[l][r][j][1],dp[l][k][j-1][1] * (pre[r]-pre[k]));
l:起點,r:終點,j:分成的部份數。0,1:最大值還是最小值。
pre陣列記錄的是字首和(這是乙個很容易想到的優化)
正解程式:
#include
#include
#include
#include
#include
#include
#define inf 0x7ffffff
using
namespace std;
typedef
long
long ll;
const ll maxn=
100;
ll n,m,a[maxn]
,pre[maxn]
;ll dp[maxn]
[maxn][20
][2]
;void
init()
}}}int
main()
for(ll i=n+
1;i<=
2*n;i++
) pre[i]=(
(pre[i-1]
+a[i-n])%
10+10)
%10;for
(ll l=
1;l<=n;l++
)for
(ll r=l;r<=
min(l+n-1,
2*n-1)
;r++
) dp[l]
[r][1]
[0]=dp[l]
[r][1]
[1]=
((pre[r]
-pre[l-1]
)%10+
10)%10
;for
(ll l=
1;l<=n;l++)}
}}ll ans1=inf,ans2=
-inf;
for(ll i=
1;i<=n;i++
)printf
("%lld\n%lld\n"
,ans1,ans2)
;return0;
}
轉圈遊戲 解題報告
轉圈遊戲 noip2013提高組day1 time limit 1000ms memory limit 128000k description 問題描述 circle.cpp c pas n 個小夥伴 編號從 0 到 n 1 圍坐一圈玩遊戲。按照順時針方向給 n 個位置編號,從0 到 n 1。最初,...
A 取石子遊戲(解題報告)
a 取石子遊戲 time limit 1000msmemory limit 10000kb64bit io format i64d i64u submit status description 有兩堆石子,數量任意,可以不同。遊戲開始由兩個人輪流取石子。遊戲規定,每次有兩種不同的取法,一是可以在任意...
取石子遊戲解題報告
有兩堆石子,數量任意,可以不同。遊戲開始由兩個人輪流取石子。遊戲規定,每次有兩種不同的取法,一是可以在任意的一堆中取走任意多的石子 二是可以在兩堆中同時取走相同數量的石子。最後把石子全部取完者為勝者。現在給出初始的兩堆石子的數目,如果輪到你先取,假設雙方都採取最好的策略,問最後你是勝者還是敗者。in...