問題
背景 background
noip2023年提高組第三道
描述 description
帥帥經常跟同學玩乙個矩陣取數遊戲:對於乙個給定的n*m的矩陣,矩陣中的每個元素a[i,j]均為非負整數。遊戲規則如下:
1. 每次取數時須從每行各取走乙個元素,共n個。m次後取完矩陣所有元素;
2. 每次取走的各個元素只能是該元素所在行的行首或行尾;
3. 每次取數都有乙個得分值,為每行取數的得分之和,每行取數的得分 = 被取走的元素值*2^i,其中i表示第i次取數(從1開始編號);
4. 遊戲結束總得分為m次取數得分之和。
帥帥想請你幫忙寫乙個程式,對於任意矩陣,可以求出取數後的最大得分。
輸入格式 input format
輸入檔案game.in包括n+1行:
第1行為兩個用空格隔開的整數n和m。
第2~n+1行為n*m矩陣,其中每行有m個用單個空格隔開的非負整數。
輸出格式 output format
輸出檔案game.out僅包含1行,為乙個整數,即輸入矩陣取數後的最大得分。
分析顯然是一道動規題,怎麼辦捏?仔細想一下,我們發現,這個矩陣的n行互不影響,m次取數對於每行的最優值都是沒有影響的,所以我們可以考慮將矩陣變成數列,逐次對美一行進行動規,那麼顯然,最後的答案就是每行的最優值之和。
一旦我們有了如上的思路,事情就變得很簡單了。方程f[i,j]表示去了i次,從左邊取走了j個。我們可以得到一下的轉移方程f[i,j]:=max要對次方陣列進行預處理。然後求解
由於題目的資料範圍,所以要用壓四位的高精度。
反思自己的方程一開始存在問題,尤其是第二種情況時a陣列的下標沒有找對。以後要細心,仔細推。
這個題看似很難,其實可以轉化為很簡單的問題,關鍵是能看出問題的本質,找出方程。不要被慣性思維限制,敢於想,敢於轉化問題。
code
program liukee;type
arr=array[0..100] of longint;
var f:array[-1..81,-1..81] of arr;
a:array[1..80] of longint;
cifang:array[0..80] of arr;
ans,tt:arr;
n,m,i,j,r,k,zu:longint;
function max(a,b:arr):arr;
var z:integer;
begin
if a[0]>b[0] then
begin
max:=a;
exit;
end;
if b[0]>a[0] then
begin
max:=b;
exit;
end;
for z:=a[0] downto 1 do
begin
if a[z]>b[z] then
begin
max:=a;
exit;
end;
if b[z]>a[z] then
begin
max:=b;
exit;
end;
end;
max:=a;
end;
operator *(a:arr;x:longint)c:arr;
var i:longint;
begin
fillchar(c,sizeof(c),0);
c[0]:=a[0];
for i:=1 to c[0] do
c[i]:=a[i]*x;
for i:=1 to c[0] do
begin
inc(c[i+1],c[i] div 10000);
c[i]:=c[i] mod 10000;
end;
while(c[c[0]+1]>0)do inc(c[0]);
end;
operator +(a,b:arr)c:arr;
var i:longint;
begin
fillchar(c,sizeof(c),0);
if a[0]>b[0] then c[0]:=a[0]
else c[0]:=b[0];
for i:=1 to c[0] do inc(c[i],a[i]+b[i]);
for i:=1 to c[0] do
begin
inc(c[i+1],c[i] div 10000);
c[i]:=c[i] mod 10000;
end;
while(c[c[0]+1]>0) do inc(c[0]);
end;
procedure outit(a:arr);
var i:longint;
begin
write(a[a[0]]);
for i:=a[0]-1 downto 1 do
begin
write(a[i] div 1000);
write(a[i] div 100 mod 10);
write(a[i] div 10 mod 10);
write(a[i] mod 10)
end;
writeln;
end;
begin
assign(input,'in.txt');reset(input);
readln(n,m);
cifang[0][0]:=1; cifang[0][1]:=1;
for i:=1 to m do cifang[i]:=cifang[i-1]*2;
fillchar(ans,sizeof(ans),0);
for zu:=1 to n do
begin
fillchar(f,sizeof(f),0);
for i:=1 to m do read(a[i]);
for i:=1 to m do
for j:=0 to m do
begin
f[i,j]:=max(f[i,j],f[i-1,j-1]+cifang[i]*a[j]);
f[i,j]:=max(f[i,j],f[i-1,j]+cifang[i]*a[m-i+j+1]);
end;
fillchar(tt,sizeof(tt),0);
for i:=1 to m do tt:=max(tt,f[m,i]);
ans:=ans+tt;
end;
outit(ans);
end.
NOIP2007 矩陣取數遊戲
傳送門給定乙個n m的矩陣,在每一行中取m次數,每次取數只能從行首或尾取數,第i次取數的貢獻是 2 i 該點值 操作n行,求最大答案 n,m 80 不難發現行與行之間是完全獨立的,所以單獨處理每一行就好了 考慮區間dp 設 dp i j 表示區間i j的答案最大值 則有 dp i j max num...
NOIP2007 矩陣取數遊戲
鏈結 簡單dp 毒瘤高精 顯然行與行沒有關聯 所以只需要每行處理 考慮dp i,j,k 表示第i次取數,第j行,有k次取的頭的最大分數 直接dp即可 includeusing namespace std define ll int128inline intread while ch 0 ch 9 d...
noip2007矩陣取數遊戲 2008 11 5
noip2007矩陣取數遊戲 2008.11.5 注意 比大小位數不相同時,取位數大的數大 當位數相同時,要從最高位開始比較,相同,則往下比,不相同,則大的數就大。我剛開始,弄成了從最低位開始比較,故,wa了 20多分鐘 小結 這道題,我打了不下 5遍,一定要先想好,在紙上把每個細節都確定了,並確定...