1. 程式人生 > >前k大金幣(動態規劃,遞推)

前k大金幣(動態規劃,遞推)

/*
///題解寫的很認真,如果您覺得還行的話可以頂一下或者評論一下嗎?
思路:
這題複雜在要取前k大的結果,如果只是取最大情況下的金幣和,直接
動態規劃遞迴就可以,可是前k大並不能找出什麼公式,所以在二元陣列的基礎上再並上一個vector

首先:初始化最左邊和最上邊(動態規劃的邊緣)
其次:找出關係,每個格的金幣只可能來自上邊或者右邊(動態規劃的狀態方程)
然後:我們要找的是前k大金幣總和而不是前1大,所以準備vector存更多情況
然後:每次處理時,當前格子除了拿上自己的金幣外,還要接受前面或者上邊送來
        的一袋袋金幣這些金幣,這些袋子有大有小,儘可能挑出前k大的袋子(如果沒有
        k那麼多就全部挑出來),然後當前格子最多接受k+k袋金幣(上面的k和左邊的k)
        接受時邊接受邊排序,那麼下次當前格子附近的格子要呼叫這個格子的金幣袋子
        情況時找出前k大即可
最後:f[m][n]這個最右下角的格子可能積累了一堆金幣,從後往前(從大到小)挑出
        k個袋子即可

*/ //一個學長(棟神)出的題 #include<bits/stdc++.h> #define ll long long using namespace std; const ll maxn=100; vector<ll>f[maxn][maxn];//f向量用來存每個位置前k大的總金幣 ll a[maxn][maxn];//a陣列用來存入資料 int main() { ///輸入環節 ll m,n,k; cin>>m>>n>>k; for(ll i=1;i<=m;i++) for(ll j=1;j<=n;j++) scanf(
"%lld",&a[i][j]);//輸入金幣情況 ///處理環節 f[1][1].push_back(a[1][1]); //先向f向量中新增初始金幣 //同樣的,接下來兩個for分別初始化向量左邊和上面兩個邊界的金幣數 for(ll i=2;i<=m;i++) f[i][1].push_back(a[i][1]+(f[i-1][1])[0]); for(ll i=2;i<=n;i++) f[1][i].push_back(a[1][i]+(f[1][i-1])[0]); //因為到最左豎和最上橫分別只有一條路徑,所以很好處理
///接下來的向量f[i][j]會一直保持從小到大的排列順序 for(ll i=2;i<=m;i++) for(ll j=2;j<=n;j++)//兩個for迴圈遍歷剩下情況 { //對f[i][j]的上面那格分析 if(k<=f[i-1][j].size())//如果要求的k比現在有的元素少 //即如果k比當前vector內元素數目小的情況 for(ll x=f[i-1][j].size()-1;x>=f[i-1][j].size()-k;x--) {//從f[i-1][j]從後往前挑出k個數(也就是最大的k個數),分別加上a[i][j],塞入f[i][j]中 //這讓f[i][j]增加了新的元素,但f[i][j]依然是從小到大排序(為後面服務) (f[i][j]).insert(upper_bound(f[i][j].begin(),f[i][j].end(),(f[i-1][j])[x]+a[i][j]),(f[i-1][j])[x]+a[i][j]); } else//如果vector內元素數目小,還不夠k多的情況 for(ll x=f[i-1][j].size()-1;x>=0;x--) {//同上 (f[i][j]).insert(upper_bound(f[i][j].begin(),f[i][j].end(),(f[i-1][j])[x]+a[i][j]),(f[i-1][j])[x]+a[i][j]); } //對f[i][j]的左邊那格分析 ///第一個if else是配套的,只執行一個,這裡又是一套if else,只執行一個 //那麼每次迴圈就處理一次上方,處理一次左邊 if(k<=f[i][j-1].size())//類似於上面,不再敘述 for(ll x=f[i][j-1].size()-1;x>=f[i][j-1].size()-k;x--) { f[i][j].insert(upper_bound(f[i][j].begin(),f[i][j].end(),(f[i][j-1])[x]+a[i][j]),(f[i][j-1])[x]+a[i][j]); } else for(ll x=f[i][j-1].size()-1;x>=0;x--) { f[i][j].insert(upper_bound(f[i][j].begin(),f[i][j].end(),(f[i][j-1])[x]+a[i][j]),(f[i][j-1])[x]+a[i][j]); } } for(ll i=f[m][n].size()-1;i>=f[m][n].size()-k;i--) printf("%lld ",(f[m][n])[i]); //從後往前數k個數,分別輸出(即在f[m][n]找出最大的k個數) }