第二题:坦克游戏1.0(方法:动态规划)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? stO ? ? ? ?在此給某位靠打01背包處理射程并AC的大神跪了 ? ? Orz
?
? ?問題描述:? ? ??
? ? ? ? ?henry公司最近推出了一款新的坦克游戲。在游戲中,你將操縱一輛坦克,在一個N×M的區域中完成一項任務。在此的區域中,
? ?將會有許多可攻擊的目標,而你每摧毀這樣的一個目標,就將獲得與目標價值相等的分數。只有獲得了最高的分數,任務才算完成。
? ?同時,為了增加游戲的真實性和難度,該游戲還做了以下的限制:
?
? ? ? ? ?(1)坦克有射程r的限制。為方便計算,射程r規定為:
? ? ? ? ? ? ? ? ? ?若坦克位于(x,y)格,則它可攻擊的目標(x1,y1)必須滿足|x-x1|,|y-y1|∈[0,r]。
? ? ? ? ?(2)對坦克完成任務的時間有嚴格限制,規定為t秒。其中,坦克每進行一次移動都需1秒的時間,每攻擊一個目標也需1秒的時
? ? ? ? ?間。時間一到t秒,便對此次任務進行記分。
? ? ? ? ?(3)坦克最初位于左上角,且移動方向只準是向右或向下,每次只允許移動一格。
? ? ? ? ?在以上的限制條件下,要完成該任務便成為了一件很難事情。因此,你必須為此編寫一個程序,讓它助你完成這個艱巨的任務。
?
? ? ? ? ?數據范圍:?1≤N、M≤50,1≤r≤10,1≤t≤250。
?
? ?輸入格式(tank.in):
? ? ? ? 第一行四個整數N、M、r、t,分別表示區域的長、寬,以及射程和完成任務時間。
? ? ? ? 接下來N行是一個N×M的矩陣,對應每個位置上目標的價值。
?
? ?輸出格式(tank.out):
? ? ? ? 輸出文件僅一個數max,即該任務中可得到的最高分數。
?
? ?輸入樣例
? ? ? ?5?5?2?7
? ? ? ?0?5?0?0?4
? ? ? ?0?0?0?0?2
? ? ? ?0?0?0?0?0
? ? ? ?0?0?0?0?0
? ? ? ?5?0?3?0?11
?
? ?輸出樣例
? ? ? ? 21
?
? ? 分析:
? ? ? ? ? 首先,文中提到的:每次只能向右,向下移動或開炮,所以基本上第一印象就是動規,所以為了完成動規就會想到下面的
? ? 一系列問題:
? ? ? ? ??
? ? ? ? ? ? ? ?射程是整道題目的關鍵,可以說沒有了射程問題就是一道普通的動規題目了,直接從1 到 n,1 到 m 循環一遍就有結果
? ? ? ? ?了,射程是一個(r+1)*(r+1) ?的正方形,如果每次打掉價值最高的,那么就可以完成對射程的處理,但于是又引出了下
? ? ? ? ?面的問題:
? ? ? ? ? ? ? ?【1】:因為對于一個目標,是只可以打一次的,也就是說這次打過,下次就得換一個了,所以不同時間同一方塊能打到
? ? ? ? ? ? ? ? 的最大價值不是固定的,要專門開一個數組來記錄并不斷更新,這就是代碼的難度直線上升。
? ? ? ? ? ? ? ?【2】:其次,對于一個決策點x,y,記上一秒x,y-1能達到的最大價值為val1,再記上一秒x,y 能達到的最大價值為
? ? ? ? ? ? ? ? val2,則這一秒在點x,y的價值可以是val1,也可以是 (val2+(此時x,y點能打到最大的價值)), 如果后者大,那么
? ? ? ? ? ? ? ? 這么做的話,勢必會選后者添加進該點,但是:設此時x,y能打到的第二大的價值為 t,那么在下一秒,同樣決策點是
? ? ? ? ? ? ? ? x, y,由于val2已經打過最大的,所以這一秒val2只能打掉次大的,所以這時記錄的最大價值為val2+最大的價值+加
? ? ? ? ? ? ? ? 次大的價值, 但如果上一秒選的是val1,這一秒它能達到的價值就是val1+最大的價值,那么就會存在一種情況:
? ? ? ? ? ? ? ? ? ? ? ? ? ?雖然val1<val2+最大價值,但val1>val2+次大價值,那么就會有val1+最大價值>val2+最大價值+次大價值!
? ? ? ? ? ? ? ? ? ? ? 也就是說,對于這個決策點x,y,上一秒的決策是最優,但下一秒卻不是最優值。這樣問題就蛋疼了— —
?
? ? ? ? ? 想到這,就應該要做出選擇了:
? ? ? ? ? ? ? ? 【1】:放棄動態規劃,選擇和搜索探討一下感情 — — 不要在一棵樹上吊死。。
? ? ? ? ? ? ? ? 【2】:霸王硬上弓— — 但就必須對射程進行一個可行的判定。直接全范圍搜顯然是不行的了— —
? ? ? ? ? 但強搜顯然是會超時的Orz(殘酷的現實),所以如何對射程進行巧妙的處理必不可少(這不是廢話嗎— —)
?
? ? 處理射程思路:
? ? ? ? ? ?仔細對每一次移動射程變化的觀察,會發現:每移動一次,范圍內就會多一組數,設決策點為x,y,若x,y由x-1,y 向下移
? ? ?動得到:即為從( x+r,y-r 到 y+r)的2r個數。這是一個可喜的發現,因為嚴格的講起來,如果對于一個出現在視野內的點,馬上
? ? ?把他打掉和之后在把它打掉是沒有差別的,所以對于這組出現在視野內的數,現在就打掉和之后打掉是沒差的,為了方便編程,完
? ? ?全可以設定為:要嗎就一進視野就馬上打掉,要嗎就一直都不打。
? ? ? ? ? ?所以每次要考慮的數就被固定了,而且相鄰兩點要考慮的不會有重合(這是非常好的,就免去的上面分析中提到的為了判重而
? ? ?不斷更新的問題),并且上面分析的前一秒最憂后一秒不一定最優的問題就不存在了。因為做的時候采用一次性添加進要打掉的點
? ? ?,也就是說點是同時添加的(影響不大,沒考慮到也沒事,只是提到可能會有這種情況)。?
? ? ? ? ? ?最后就是要對某個點,打k 個目標的得分進行預處理:
? ? ? ? ? ? ? ? ?記le ?[ i , j , k ]為從左邊到達( i ,j )這個點,打掉此時k 個新出現在視野中的目標能獲得的得分;
? ? ? ? ? ? ? ? ?記up[ i , j , k ?]為從上面到達( i , j)這個點,打掉此時k個新出現在視野中的目標能獲得的得分;
? ? ? PS:按這個思路,一開始要對已經出現在視野內的點進行預處理~~~
?
? ??代碼:??? ? ? ?
1 var 2 res:array[-20..251,-100..100,-100..100] of longint;//res是進行動規的數組, 3 //res[該時間,橫坐標,縱坐標]為該時這點得分最大值 4 val:array[-20..100,-100..100] of longint; //記錄得分點 5 up,le:array[0..51,0..51,0..100] of longint; //up,le均為 6 n,m,r,t:longint; 7 8 Function max(a,b:longint):longint; //求兩者最大值得函數,返還值為較大者 9 begin if a>b then exit(a) else exit(b); end; 10 11 Procedure init; 12 var d:array[0..200] of longint; 13 i,j,l:longint; 14 begin 15 l:=0; 16 readln(n,m,r,t); //讀入行、列、射程、限時 17 fillchar(res,sizeof(res),$ff); //初始化動規數組,賦值為-1是為了防止某些不可能情況成立 18 for i:=1 to n do 19 for j:=1 to m do read(val[i,j]); //讀入目標價值 20 for i:=1 to r+1 do //因為按照思路是考慮視野的邊界,所以一開始要處理視野內的點 21 for j:=1 to r+1 do 22 if val[i,j]>0 then begin //d數組存放該視野中所有的有分的點的分 23 inc(l); 24 d[l]:=val[i,j]; 25 end; 26 for i:=1 to l-1 do //對這個數組進行排序,因為分高的優先去 27 for j:=i+1 to l do 28 if d[i] < d[j] then begin 29 d[0]:=d[i]; d[i]:=d[j]; d[j]:=d[0]; 30 end; 31 for i :=2 to l do inc(d[i],d[i-1]); //做完這步d[x]表示的就是:打掉x 個點的最大得分 32 for i :=1 to l do res[i,1,1]:=d[i]; //初始化站在原地不動一直打的情況 33 close(input); 34 end; 35 36 Procedure fill; //這個過程就是對每個點的新增視野做類似上方的步驟,即預處理 37 var d:array[0..200] of longint; 38 i,j,k,l,p:longint; //l 存的是新增視野內有得分點的數量 39 begin 40 res[0,1,1]:=0; 41 for i:=1 to n do 42 for j:=1 to m do begin //枚舉每個點 43 //下面這部分是從上方移動到該點的情況 44 l:=0; //清空總數 45 for k:=j-r to j+r do //查找新增視野中有得分得點 46 if val[i+r,k]>0 then begin //添加進數組,總數+1 47 inc(l); 48 d[l]:=val[i+r,k]; 49 end; 50 for k:=1 to l-1 do //同樣排序 51 for p:=k+1 to l do 52 if d[k] < d[p] then begin 53 d[0]:=d[k]; d[k]:=d[p]; d[p]:=d[0]; 54 end; 55 for k:=1 to l do up[i,j,k]:=up[i,j,k-1]+d[k]; //將得到的數據存入up數組中 56 up[i,j,0]:=l; //儲存最多可以打多少個 57 //這部分是從左方移動到該點的情況 58 l:=0; //清空總數 59 for k:=i-r to i+r do //查找新增視野中有得分得點 60 if val[k,j+r]>0 then begin //添加進數組,總數+1 61 inc(l); 62 d[l]:=val[k,j+r]; 63 end; 64 for k:=1 to l-1 do //同樣排序 65 for p:=k+1 to l do 66 if d[k] < d[p] then begin 67 d[0]:=d[k]; d[k]:=d[p]; d[p]:=d[0]; 68 end; 69 for k:=1 to l do le[i,j,k]:=le[i,j,k-1]+d[k]; //將得到的數據存入up數組中 70 le[i,j,0]:=l; //儲存最多可以打多少個 71 end; 72 end; 73 74 Procedure main; 75 var i,j,z,k:longint; 76 begin 77 for z:=1 to t do //把時間做狀態 78 for i:=1 to n do //枚舉每個點 79 for j:=1 to m do 80 if i+j-2<=z then begin //邊界,因為第z 時,走到的點 i,j與1,1的距離不能超過 z 81 res[z,i,j]:=max(res[z-1,i,j],max(res[z-1,i,j-1],res[z-1,i-1,j])); //直接移動來,不管新增目標 82 for k:=1 to up[i,j,0] do if (z-1-k>0)and(res[z-1-k,i-1,j]>=0) then 83 res[z,i,j]:=max(res[z,i,j],res[z-1-k,i-1,j]+up[i,j,k]); //枚舉打多少個目標(從上面來這點的) 84 for k:=1 to le[i,j,0] do if (z-1-k>0)and(res[z-1-k,i,j-1]>=0) then 85 res[z,i,j]:=max(res[z,i,j],res[z-1-k,i,j-1]+le[i,j,k]); //枚舉打多少個目標(從上面左邊來的) 86 end else break; 87 k:=0; 88 for i:=1 to n do //循環一邊尋找最大值 89 for j:=1 to m do 90 if res[t,i,j]>k then k:=res[t,i,j]; 91 writeln(k); //輸出結果 92 end; 93 94 begin 95 assign(input,'tank.in'); assign(output,'tank.out'); 96 reset(input); rewrite(output); 97 init; fill; 98 main; close(output); 99 end. tank(動規打法)?
? ???優化:
? ? ? ? ? ?雖然根據測試時給的數據都AC的,但總耗時還是達到了1.4秒多,在完成編程后就可以想想優化:
? ? ? ? ? ? ? ? ? ? ? 在預處理up和le數組的時候,由于每次新出現在視野內的目標不多,所以一般采用選擇排序,但執行次數一多時間就
? ? ? ? ? ? ? ? 上去了— —。所以采用快排的打法并進行一定的修改就可以達到0.30秒AC全數據~,這邊貼一個標程:
1 var max,n,m,t,r:longint; 2 map,le:array[0..50,0..50] of longint; 3 s,temp:array[0..100] of longint; 4 f:array[0..50,0..50,0..250] of longint; 5 function min(x,y:longint):longint; 6 begin 7 if x<y then min:=x else min:=y; 8 end; 9 procedure sort(l,m:longint); 10 var i,j,mid,t:longint; 11 begin 12 i:=l;j:=m;mid:=temp[(l+m) div 2]; 13 repeat 14 while temp[i]>mid do inc(i); 15 while temp[j]<mid do dec(j); 16 if i<=j then begin t:=temp[i];temp[i]:=temp[j];temp[j]:=t;inc(i);dec(j);end; 17 until i>j; 18 if i<m then sort(i,m); 19 if j>l then sort(l,j); 20 end; 21 procedure init; 22 var i,j,k:longint; 23 begin 24 assign(input,'tank.in');reset(input); 25 assign(output,'tank.out');rewrite(output); 26 readln(n,m,r,t); 27 for i:=1 to n do 28 begin 29 for j:=1 to m do read(map[i,j]); 30 readln; 31 end; 32 k:=0; 33 fillchar(temp,sizeof(temp),0); 34 fillchar(le,sizeof(le),0); 35 max:=0; 36 for i:=1 to r+1 do 37 for j:=1 to r+1 do 38 begin 39 if map[i,j]<>0 then begin inc(k); 40 temp[k]:=map[i,j]; end; 41 end; 42 sort(1,k); 43 s[0]:=0; 44 le[1,1]:=k; 45 for i:=1 to k do s[i]:=s[i-1]+temp[i]; 46 if k>t then k:=t; 47 for i:=1 to k do begin f[1,1,i]:=s[i];if f[1,1,i]>max then max:=f[1,1,i];end; 48 end; 49 procedure main; 50 var i,j,k,p,ir,il,jr,jl,long,ans:longint; 51 begin 52 for i:=1 to n-r do 53 for j:=1 to m-r do 54 if (i<>1) or (j<>1) then 55 begin 56 if i>1 then 57 begin 58 if i+r<=n then ir:=i+r else ir:=n; 59 if j+r<=m then jr:=j+r else jr:=m; 60 if i-r>=1 then il:=i-r else il:=1; 61 if j-r>=1 then jl:=j-r else jl:=1; 62 long:=0; 63 for k:=1 to jr-jl+1 do if map[ir,k+jl-1]<>0 then 64 begin inc(long);temp[long]:=map[ir,k+jl-1];end; 65 sort(1,long); 66 s[0]:=0; 67 fillchar(s,sizeof(s),0); 68 for k:=1 to long do s[k]:=s[k-1]+temp[k]; 69 ans:=min(le[i-1,j]+long,t-(i+j-2)); 70 if ans>le[i,j] then le[i,j]:=ans; 71 for k:=1 to ans do 72 begin 73 f[i,j,k]:=0; 74 for p:=0 to min(long,k) do 75 begin 76 if f[i-1,j,k-p]+s[p]>f[i,j,k] then 77 f[i,j,k]:=f[i-1,j,k-p]+s[p]; 78 end; 79 if f[i,j,k]>max then max:=f[i,j,k]; 80 end; 81 end; 82 if j>1 then 83 begin 84 if i+r<=n then ir:=i+r else ir:=n; 85 if j+r<=m then jr:=j+r else jr:=m; 86 if i-r>=1 then il:=i-r else il:=1; 87 if j-r>=1 then jl:=j-r else jl:=1; 88 long:=0; 89 for k:=1 to ir-il+1 do if map[k+il-1,jr]<>0 then 90 begin inc(long);temp[long]:=map[k+il-1,jr];end; 91 sort(1,long); 92 s[0]:=0; 93 fillchar(s,sizeof(s),0); 94 for k:=1 to long do s[k]:=s[k-1]+temp[k]; 95 ans:=min(le[i,j-1]+long,t-(i+j-2)); 96 if ans>le[i,j] then le[i,j]:=ans; 97 for k:=1 to ans do 98 begin 99 for p:=0 to min(long,k) do 100 begin 101 if f[i,j-1,k-p]+s[p]>f[i,j,k] then 102 f[i,j,k]:=f[i,j-1,k-p]+s[p]; 103 end; 104 if f[i,j,k]>max then 105 begin max:=f[i,j,k];end; 106 end; 107 end; 108 end; 109 writeln(max); 110 close(input); 111 close(output); 112 end; 113 begin 114 init; 115 main; 116 end. tank(急速優化版)? ? ? ? ? ?PS:該代碼無注釋,但了解它的思想就看得懂了~
?
? ? ?后記:
? ? ? ? ? ?一、如文章第一句 — — 用01背包處理新出現在視野中的點也是可以A的— —只比優化慢0.2秒。還可以用貪心— —。
? ? ? ? ? ? ? ? ?反正解決了對視野的處理,將其轉化為新視野后就八仙過海,各顯神通啦— — 所以不要拘泥于一種算法— —,還是
? ? ? ? ? ? ? ? ?那句話:不要在一棵樹上吊死— —
? ? ? ? ? ?二、總的來說,無論是對視野的處理還是解決視野問題后的預處理都讓我收獲了很多,也意識到了自己的很多不足Orz。。
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??END。
?
轉載于:https://www.cnblogs.com/qq359084415/p/3378274.html
總結
以上是生活随笔為你收集整理的第二题:坦克游戏1.0(方法:动态规划)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows2008C盘清理
- 下一篇: Pechkin:html - pdf 利