目錄2. 動態規劃
3. 一半dfs一半dp
總結時間到!
在乙個n行m列的矩陣中,選出乙個r行c列的子矩陣,使相鄰元素的差的絕對值的和最小。解題思路
在乙個n行m列的矩陣中,選出乙個 \(r\) 行 \(c\) 列的子矩陣,相當於乙個 \(n\) 行 \(m\) 列的矩陣,在 \(n\) 行中選出 \(r\) 行,在 \(m\) 列中選出 \(c\) 列,選出的行和列相交的點就是選出的數。
所以乙個很簡單的辦法,就是列舉 \(n\) 行中選哪 \(r\) 行, \(m\) 列中選哪 \(c\) 列。這就是乙個組合問題,相當於列舉 \(r \choose n\) 和 \(c \choose m\)。
這個演算法的時間複雜度,就是列舉的 \(r \choose m\)
\(\times\)
\(c \choose m\) 在乘上計算的 \(r \times c\) ,剛好可以得60分。
**大致是這樣實現的:
列舉 \(r \choose m\)
列舉 \(r \choose m\)
在第二步列舉完後求值,更新答案
列舉組合,可以用dfs來實現:
int n, r, a [ maxn + 1 ];
void solve ( int step )
for ( int i = a [ step - 1 ] + 1; i <= n; i ++ ) a [ step ] = i, solve ( step + 1 );
}
你可以拿這個**去實現一些題目(當然不行),你會發現,在 \(n<=15\) 的時候大致可以過(主要看評測機是賽揚還是i99900k),但是你可以試一試這道題,tle的很厲害。
仔細觀察i的範圍,這裡顯然可以做一些優化。
"\(i <= n\)" 這個範圍很不合理,如果剩餘空間不夠 \(r - step\) 個,肯定不可能。
因此,\(n - i\) 必須 \(>= r - step\),轉化得到 \(i <= n - r + step\)。
**只用修改一處,就是把for ( int i = a [ step - 1 ] + 1; i <= n; i ++ )
變為for ( int i = a [ step - 1 ] + 1; i <= n - r + step; i ++ )
。
**1回到這道題目上,**實現很簡單:
//主程式請自行實現
void dfs1 ( int step )
for ( int i = row [ step - 1 ] + 1; i <= n - r + step; i ++ ) row [ step ] = i,dfs1 ( step + 1 ); }
void dfs2 ( int step )
for ( int i = col [ step - 1 ] + 1; i <= m - c + step; i ++ ) col [ step ] = i,dfs2 ( step + 1 );
}
如果用dp,可以 參考其他題目 這樣寫:
\(f [ n ] [ r ] [ m ] [ c ]\) 表示 \(n\) 列中取 \(r\) 列,\(m\) 列中取 \(c\) 列,第 \(n\) 行和第 \(m\) 列必須取時的最小分值。
但這樣寫會出現狀態不明確的問題,也就是有後效性。
不能全部用dp,那咋辦???
別急,看後面 》
解題思路
既然全部dp或者記憶化狀態會不明確,那麼通過再列舉就可以確定狀態了(這裡選擇列)。
\(f [ i ] [ j ]\) 表示 \(i\) 列中取 \(j\) 列,第 \(i\) 列必需取的最小分值
\(f [ i ] [ j ] = \min_ ^ + \sum_ ^ }\)
(\(2 \leq i \leq m, 2 \leq j \leq min ( i, c ), j - 1 \leq k < i\))
這樣寫出的dp大致是這麼一坨東西:
/*
請先預處理:
col [ i ] 表示i列內部的分值(相當於第乙個sigma)
cc [ i ] [ j ] 表示第i列與第j列相鄰時,他們列之間相差的分值(相當於第二個sigma)*/
for ( int i = 2; i <= m; i ++ )
for ( int j = 2; j <= min ( i, c ); j ++ )
for ( int k = j - 1; k < i; k ++ )
f [ i ] [ j ] = min ( f [ i ] [ j ], f [ k ] [ j - 1 ] + col [ i ] + cc [ k ] [ i ] );
再結合一下之前的dfs,寫出**:
**2
//同樣,主程式請自行實現
//碼風小清新,多多照顧
//適當做了一些優化(630ms)
void dfs ( int x )
}} for ( int i = 1; i <= m; i ++ )
}for ( int i = 1; i <= m; i ++ )
for ( int i = 2; i <= m; i ++ )
}} for ( int i = c; i <= m; i ++ )
return;
} for ( int i = row [ x - 1 ] + 1; i <= n - r + x; i ++ )
}
誒,這道題主要是乙個dfs和dp結合的玩意。
當發現dfs轉dp會產生後效性時,有兩種方法:
加狀態再搜尋
第一種方法顯然不現實 十六維陣列,為啥不給把16g記憶體全用上呢?(手滑=手動滑稽
那就只能用第二種方法,就產生了這一坨dfs+dp的東東。
解這種煩人的村民題時,一般先打乙個暴力(noip2014,如果前三題ac,一等獎穩穩的),再去優化成dp or 記憶化。
哎嗎,這篇題解寫了我好幾年喲,贊乙個唄
數論之矩陣解題報告2
hdu 2256 不得不感嘆一下,牛 x的題,牛 x的解法,開頭做的時候直接用 double 二分冪求解,但是很不幸測試樣例都沒有過,估計是精度不夠,到 n 5的時候就差了好多,但是如果把這個答案分成兩個部分分別用整數表示呢?那就能很好的解決了精度的問題,本題就很巧妙的運用了共軛數 hdu 1568...
BZOJ 3288 Mato矩陣 解題報告
這個題好神呀。orz taorunz 有乙個結論,這個結論感覺很優美 ans prod varphi i 至於為什麼呢,大概是這樣子的 對於每個數字 x 第 x 行有 x varphi x 個數字不為 1 則說明這一行要被消 x varphi x 次 別忘了每一行都會被 1 給消一次 每次消元都會令...
解題報告 NOIP2015 子串
這是一道dp題,然後來想想怎麼表示狀態,對答案有影響的就是a串的第i個字元,b串的第j個字元,和k個子串,簡單來說就是和選取的字元和子串的數量有關.那麼設 f i j kk 表示在a串的前i個字元中選kk個子串匹配b串的前j個字元的方案數.求方案數可以採用加法原理,考慮a串的第i個字元,那麼這個字元...