[最大費用最大流] [記憶化搜尋] [Vijos P1653] 瘋狂的方格取數 (getnum)
背景 Background
Due to the talent of talent123,當talent123做完NOIP考了兩次的二取方格數和vijos中的三取方格數後,突發奇想….
題目描述 Description
在一個寬
M ,長N 的矩陣中,請你編一個程式,n 次從矩陣的左上角走到矩陣的右下角,每到一處,就取走該處的數字,請你選擇一
種走法使取得的數字的和最大,並輸出其最大值。其中:3<=M<=20,M<=N<=100,1<=n<=10
如輸入資料:
3 10 13
0 1 2 3 4 9 7 1 3 1
9 1 2 2 3 6 7 8 1 2
1 2 3 4 5 9 8 7 6 1
9 7 1 3 1 9 1 2 2 3
6 7 8 1 2 1 2 3 4 5
9 1 2 2 3 6 7 8 1 2
1 2 3 4 5 9 8 7 6 1
9 7 1 3 1 9 1 2 2 3
6 7 8 1 2 1 2 3 4 5
9 1 2 2 3 6 7 8 1 2
1 2 3 4 5 9 8 7 6 1
9 7 1 3 1 9 1 2 2 3
6 7 8 1 2 1 2 3 4 0
其中n =3,M=10,N=13
即當n=3 時,就相當於是3取方格數。
對於以上的資料:
將輸出:297
//注:如過你想到了無記憶性搜所的方法(不管你怎樣優化),你可以直接放棄這道題了。
//提示1:動態規劃如果用的是二位陣列,規模為100∗100000 即可。
//提示2:如果你堅信自己的程式已經無可優化了,可有2 個數據依然超時,那麼告訴你,存在資料有M<n 的情況!!!
輸入 Input
第一行:三個整數:
n,M,N
以下的N 行每行M 個數字,代表你要處理的矩陣。
輸出 Output
只有一行:你所取得的數字的和。
樣例輸入 Sample Input
4 6 7
0 2 3 4 5 6
6 5 4 3 2 1
0 9 8 7 6 5
12 3 4 5 6 7
0 0 0 1 2 3
12 23 34 45 1 23
4 5 6 6 1 0
樣例輸出 Sample Output
265
限制 Limits
資料範圍見題目
共有10 個測試資料,每個測試資料包含1 個測試點,每個測試點的時間限制為2 秒鐘。
Time Limit :2s & Memory Limit :128MB
來源 Source
本題目來自:北京市,中關村中學,高三9班,孫一(網名:talent123),聯絡方式:865383864(QQ)
這道題是PoPoQQQ在培訓時講的,直接上最大費用最大流,模板水過……
思路:拆點建邊,一個格子拆成兩個點,這兩點之間有一條流量為
連邊注意不要溢位範圍……(別連到外面去)
還有超級源和超級匯的選擇
然後就是程式碼了……
Code
題解貌似是記憶化搜尋?
#include <stdio.h>
#include <math.h>
int move[100][100000]={};//move[step][status];//status是一個 M進位制, time位 的狀態儲存量;
int time,map[100][20],M,N;//橫 M <20,豎 N<100,M<N,規模:M+N<=100,time<=10,((2M)^time)*(M+N)<=10000000;
int trial;//trial=2^time; //trial是一個 2進位制,time位 的增量儲存變數 ;
int fp(int step,int status)
{
if(move[step][status]!=0)return move[step][status];//曾經已經求出過此狀態的值 ;
int temp=0,max;//temp:此狀態下,覆蓋的值! max:從此開始(包括此狀態)一直到終止狀態的最大值,賦temp為初值!;
int flag[100][20]={};//一個位置的值是否被取過了;
int rem=status,i;
int list[10];
for(i=0;i<time;i++)//計算:temp;
{
int x=rem%M,y=step-x; //翻譯 ;
list[i]=x;
if(flag[y][x]==0) //之前這個地方沒有被取過
{
temp+=map[y][x];
flag[y][x]=1;
}
rem/=M;
} //for
max=temp;
for(i=0;i<trial;i++)//列舉下一層搜尋中所有單位的 (0--1)橫向增量狀態:i;
{
//計算新編碼;
int flag2=0;//flag==0則新編碼合法,flag==1則新編碼越界;
int p=i,j;
int new_stitus=0;//新狀態編碼;
for(j=0;j<time;j++)//對於增量編碼 p(from:i),計算新的狀態編碼 ;
{
int increase=p%2;
if( (increase==0?(step-list[time-j-1]+1):(list[time-j-1]+1))>=(increase==0?N:M) )//是否越界?
{//如果越界 ;
flag2=1;//標誌越界 ;
break;
}
//轉錄 ;
new_stitus*=M;
new_stitus+=list[time-j-1]+increase;
p=p/2;
}//for
if(flag2==0&&max<temp+fp(step+1,new_stitus))//不越界
{
max=temp+fp(step+1,new_stitus);//更新 max 的值(同時進行狀態的轉移)
}
}//for
move[step][status]=max;//儲存此狀態的結果 ;
return max;
}
int main(void)
{
int i,j;
scanf("%d%d%d",&time,&M,&N);
if(M<time)time=M;
//壓縮(當M<time時,更快的方法是直接輸出map上所有數的簡單相加和,
//由於資料規模比2S運算量小一個數量級,故這樣也可以得滿分);
for(i=0;i<N;i++)
for(j=0;j<M;j++)
scanf("%d",&map[i][j]);
trial=(int)pow(2,time);//0---(trail-1)即所有的0--1狀態 ;
printf("%d",fp(0,0));
return 0;
}