1. 程式人生 > >【寒江雪】動態規劃小結

【寒江雪】動態規劃小結

前言

  大概是從5月11號開始在賽碼網上刷題,這個網站上有的題目樣例覆蓋面不全。當然了,不能過於強求,能有這麼個網站練題還是挺好的。練題無論在哪練目的是為了通過題目提升自己.
  相比於POJ和HDU等各大知名OJ而言,還是比較簡單了。所以還是建議到POJ上練練題。
  但這幾天在賽碼網上練題也不是沒有收穫。其中除了簡單的模擬題之外。還有基礎動態規劃的題目若干道,以及一道我從大一上尚未解決直到前幾天才解決的一道題。我會先從這道題開始寫。然後總結這幾天做的DP.

三分線

題目描述
小賽很喜歡看A隊和B隊的籃球比賽。眾所周知,籃球每回合根據投籃遠近可以得2分或3分。如果投籃距離小於d那麼得2分,大於等於d得3分。我們將d記為三分線。

每次小賽都喜歡通過改變三分線的大小來讓自己支援的A隊獲取更大的優勢。現給出兩個隊伍投籃得分的距離,小賽希望你能夠幫他選擇一個合理的三分線,使得A隊優勢最大。

輸入


輸入資料包含兩行。
第一行第一個數為n(1≤n≤2*105), 表示A隊進球數,接下來n個正整數表示A隊每次進球的投籃位置ai(1≤ai≤2*109)。
第二行第一個數為m(1≤m≤2*105),表示B隊進球數,接下來m個正整數表示B隊每次進球的投籃位置bi(1≤bi≤2*109)。

輸出
一個整數,表示A隊得分減去B隊得分的最大值

樣例輸入
3 1 2 3
2 5 6

樣例輸出
3

解題思路
  這道題的題目描述的是如何確定d使得A隊取得的優勢更大。優勢大的定義為A隊減去B隊得分的最大值。顯然,這裡和Codeforces上那道題還是有點不一樣的。稍後我會將題號貼出。
  剛拿到這道題的時候,首先想到先將A,B兩隊的投射位置從小到大排序,排完序之後再從最小到最大列舉一遍,依次確定每個d的位置,判斷哪個d的位置更好。這顯然是超時的方法,而在列舉的過程中,做了很多不必要的判斷——不是每個d都要判斷的,因為在[ai
,ai+1)之間的d值都是等效的。因此,我們只需要列舉A隊的每一個投射距離,判斷該距離下A隊得分與B隊得分,然後判斷是否比之前的答案更優,如果更優則更新答案。最後得到的就是最優解。
  最後我得出的方案是:列舉A隊的投球距離a1,a2…an。對於每一個ai,從B對投射距離b1,b2…bn中找到一個bj,使得bj-1<=ai,bj>=ai,然後記錄less[i]=j-1,表示到A隊的第i個距離為止,B隊中有j-1個距離小於它。這樣在列舉到A隊的第i個距離時,可以很輕鬆的算出A隊的分數和B隊的分數。總時間複雜度為O(nlogn+mlogm)+O(nlogm)+O(n)
  Codeforces相似題號:493C
  程式碼如下:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<assert.h>
#include<algorithm>
#include<set>
using namespace std;
//unsigned long long n, m;
unsigned long long gcd(unsigned long long x, unsigned long long y) {
    return y == 0 ? x : gcd(y, x%y);
}
int n, m;
int A[222222];
int B[222222];
int Less[22222];
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &A[i]);
    }
    scanf("%d", &m);
    for (int i = 0; i < m; i++) {
        scanf("%d", &B[i]);
    }
    sort(A, A + n);
    sort(B, B + m);
    memset(Less, 0, sizeof Less);
    for (int i = 0; i < n; i++) {
        int *p = upper_bound(B, B + m, A[i]);
        Less[i] = p - B;
    }
    Less[n] = m;
    int A_Total;
    int B_Total;
    int ans = -0x3f3f3f3f;
    for (int i = 0; i <=n; i++) {
        A_Total = 3 * (n - i) + 2 * i;
        B_Total = 3 * (m - Less[i]) + 2 * Less[i];
        ans = max(ans, A_Total - B_Total);
    }
    cout << ans << endl;
    return 0;
}

上臺階

題目描述
有一樓梯共m級,剛開始時你在第一級,若每次只能跨上一級或二級,要走上第m級,共有多少走法?
注:規定從一級到一級有0種走法。

輸入
輸入資料首先包含一個整數n(1<=n<=100),表示測試例項的個數,然後是n行資料,每行包含一個整數m,(1<=m<=40), 表示樓梯的級數。

輸出
對於每個測試例項,請輸出不同走法的數量。

樣例輸入
2
2
3

樣例輸出
1
2

解題思路
  這道題是前年寒假集訓做的一道DP入門題了。想想也是懷念啊。這道題的解題思路如下:假設dp[i]為走到第i級的走法.dp[i]=dp[i-1]+dp[i-2].初始化dp[1]=1;dp[2]=1;。
  程式碼如下:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<assert.h>
#include<algorithm>
#include<set>
#include<bitset>
#include<fstream>
#include<map>
#include<sstream>
#include<stack>
using namespace std;

int main() {

    int n;
    cin >> n;
    while (n--) {
        int dp[1111];
        int m;
        memset(dp, 0, sizeof dp);
        dp[1] = 1;
        dp[2] = 1;
        cin >> m;
        for (int i = 3; i <= m; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        cout << dp[m] << endl;
    }
    return 0;
}

文藝青年愛文學

題目描述
小賽是一名文藝的程式設計師,他十分熱愛文學。乘車去公司應聘的路上,小賽又在構想自己的詩歌了——

“啊!小賽啊小賽!帥啊很帥可帥了!
啊!小賽啊小賽!棒啊很棒可棒了!
啊!小賽啊小賽!啊啊啊啊啊啊啊!”

儘管小賽的詩歌——額……有那麼一點——(啊啊別攔我——讓我掐死這隻小賽!)……
但是,小賽自己還是深深陶醉其中的。

小賽現在想要創作一首恰好為一定字數(共有n個能滿足要求的字數,達到任一皆可)的新詩歌……
他會構想m種長度的短句(如上面那首“詩歌”,有長度為1和7的短句),構想每種長度的短句所耗費的時間是相同的。
現在讓你幫忙算下,小賽最少需要多少時間,才能達成自己的目標呢?如果沒有辦法實現,請輸出”It is not true!”(沒有引號)。


輸入
第一行一個整數n,表示小賽想創作詩歌的字數的集合大小。
接下來n行,其中第i行為一個數a[i],表示這首詩歌可以是a[i]個字。
第二行一個整數m,表示小賽可以構想出m種不同長度短句的個數。
接下來m行,其中第i行為兩個整數b[i],c[i],其中b[i]表示小賽可以創作出長度為b[i]的短句,對應的c[i]表示創作這種長度短句所消耗的時間。

資料保證——a[]中的數各不相同,b[]中的數各不相同,c[]不超過10000.
資料保證——
對於30%的測試點,n=1,1<=m<=2,1<=a[],b[]<=20,
對於70%的測試點,1<=n,m<=5,1<=a[],b[]<=100;
對於100%的測試點,1<=n,m<=100,1<=a[],b[]<=10000;


輸出
輸出一行,
如果有辦法達成目標,則輸出一個整數,表示達成目標所最少需要的創作時間。
如果沒有辦法達成目標,則輸出”It is not true!”(沒有引號)


樣例輸入
2
7
11
2
3 1
2 100


樣例輸出
103


解題思路
  這道題說的這個文藝青年可以創作a1,a2…an長度的詩,但這些詩要由若干短句組成.但這個人只能通過創作一系列短句來達到目的。但是創作短句需要消耗時間。這個人只需要創作一個長句就行了,問他創作的最小消耗時間。
  可見這道題是一道典型的完全揹包變式題。
  建立狀態,從dp[j]表示創作長度為j的長句需要的最短時間。初始化dp陣列為inf(很大很大的值),dp[0]=0。對於每一個bi,如果j>=bi,則dp[j]=min(dp[j-bi]+ci,dp[j])
  最後,答案為min(dpi).
  程式碼如下:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<assert.h>
#include<algorithm>
#include<set>
using namespace std;
int n, m;
long long a[111];
long long b[111];
long long c[111];
long long ans = -1;
long long dp[11111];
int main() {
    cin >> n;
    long long mx_a = -1;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        mx_a = max(a[i], mx_a);
    }
    cin >> m;
    for (int i = 0; i < m; i++) {
        cin >> b[i] >> c[i];
    }

    memset(dp, 0x3f3f3f3f3f3f3f3f, sizeof dp);
    dp[0] = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 1; j <= mx_a; j++) {
            if (j >= b[i]) {
                dp[j] = min(dp[j - b[i]] + c[i],dp[j]);
            }
        }
    }
    ans = 0x3f3f3f3f;
    for (int i = 0; i < n; i++) {
        ans = min(ans, dp[a[i]]);
    }
    if(ans!=0x3f3f3f3f)
        cout << ans << endl;
    else {
        cout << "It is not true!" << endl;
    }
    return 0;
}

接金幣

題目描述
小賽非常喜歡玩遊戲,最近喜歡上了一個接金幣的遊戲。在遊戲中,使用帽子左右移動接金幣,金幣接的越多越好,但是金幣掉到地上就不能再接了。為了方便問題的描述,我們把電腦螢幕分成11格,帽子每次能左右移動一格。現在給電腦螢幕如圖示上座標:

也就是說在遊戲裡,金幣都掉落在0-10這11個位置。開始時帽子剛開始在5這個位置,因此在第一秒,帽子只能接到4,5,6這三個位置中其中一個位置上的金幣。問小賽在遊戲中最多可能接到多少個金幣?(假設帽子可以容納無窮多個金幣)。


輸入
輸入資料有多組。每組資料的第一行為以正整數n (0<n<100000),表示有n個金幣掉在螢幕上上。在結下來的n行中,每行有兩個整數x,T (0<T<100000),表示在第T秒有一個金幣掉在x點上。同一秒鐘在同一點上可能掉下多個金幣。n=0時輸入結束。輸入資料以空格隔開


輸出
每一組輸入資料對應一行輸出。輸出一個整數m,表示帽子最多可能接到m個金幣。


樣例輸入
7
6 3
8 2
9 3
7 1
6 2
5 1
7 2

樣例輸出
3

  這道題也是前年寒假集訓的訓練題之一。這道題的主角只能從位置5出發。每次移動只能選擇向左,向右或者不移動三個選項。他的目標是儘可能多地去接金幣。而金幣的出現是與時間相關的。
解題思路
  解決該問題的出發點就是按時間模擬每一個位置的情況。對於該問題的情況還應該考慮到一點:玩家初始時位於位置5,因此(abs(currentPos-5)<=T).假設一個dp[t][p]陣列,表示在第t秒,主角位於位置p獲得的最大金幣數,gold[t][p]表示第t秒位置p處的金幣數.dp[t][p]=max(dp[t-1][p-1],dp[t-1][p],dp[t-1][p+1])+gold[t][p].
  但是這裡的二維陣列完全可以轉換為一維陣列,因為每一次狀態轉移只涉及上一次狀態,因此可以使用dp[2][p]陣列來做這道題。
  程式碼如下:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<assert.h>
#include<algorithm>
#include<set>
using namespace std;
long long dp[2][11];
long long a[111111][11];
int main() {
    int n;
    cin >> n;
    int x, t;
    int mxt = -1;
    for (int i = 0; i < n; i++) {
        cin >> x >> t;
        a[t][x]++;
        mxt = max(t, mxt);
    }
    memset(dp, 0, sizeof dp);
    long long ans = -1;
    int k = 0;
    for (int i = 1; i <= mxt; i++) {
        for (int j = 0; j < 11; j++) {
            if (abs(j - 5) <= i) {
                long long left = -1;
                long long right = -1;
                if (j > 0)left = dp[1-k][j - 1];
                if (j < 10)right = dp[1-k][j + 1];
                long long mx = max(dp[1-k][j], max(left, right));
                dp[k][j] = mx+a[i][j];
                ans = max(dp[k][j], ans);
            }
        }
        k = 1 - k;
    }
    cout << ans << endl;
    return 0;
}

攔截導彈

題目描述
某國進行軍事演戲,研發一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於等於前一發的高度。某天,雷達捕捉到敵國導彈來襲。由於該系統還在試用階段,所以只用一套系統,因此有可能不能攔截所有的導彈。請你幫忙選擇一套系統,根據測試的導彈數量和每次導彈飛來的高度,計算出最多能攔截導彈的數目。


輸入
第一行輸入測試資料組數N(1<=N<=10)
接下來一行輸入這組測試資料共有多少個導彈m(1<=m<=20)
接下來行輸入導彈依次飛來的高度,所有高度值均是大於0的正整數。


輸出
輸出最多能攔截的導彈數目


樣例輸入
2
8
389 207 155 300 299 170 158 65
3
88 34 65

樣例輸出
6
2

解題思路
  這道題乍一看還以為只是簡單的模擬題,但仔細一想並不是。每一次輸入給出一系列導彈的高度,每一次攔截都會拉低系統的最高射程。這題可以抽象為在一個序列中找出最長下降子序列。利用動態規劃的方法求解這道題是很好的。同樣定義一維陣列dp,dp[i]表示到第i顆導彈為止,系統能攔截的最大導彈數。狀態轉移方程為dp[i]=max(dp[i],dp[j]+1)(h[j]>h[i],j=1-(i-1)).初始化dp陣列為0
  程式碼如下:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<assert.h>
#include<algorithm>
#include<set>
#include<bitset>
#include<fstream>
using namespace std;
int n, m;
int h[1111];
int dp[1111];
int main() {
    cin >> n;
    while (n--) {
        cin >> m;
        memset(dp, 0, sizeof dp);
        memset(h, 0, sizeof h);
        for (int i = 1; i <= m; i++) {
            cin >> h[i];
        }
        dp[1] = 1;
        for (int i = 2; i <= m; i++) {
            dp[i] = 1;
            int mxPre=0;
            for (int j = m - 1; j >= 1; j--) {
                if (h[i]<h[j] && dp[j]>mxPre)
                {
                    mxPre = dp[j];
                }
            }
            dp[i] = max(mxPre + 1, dp[i]);
        }
        cout << dp[m] << endl;
    }
    return 0;
}

組合概率

題目描述
某生產零件的工廠為方便管理場內生產的零件種類,現將他們生產的零件從低等到高等零件排序,序號分別為1,2..n,已知該廠的任意幾個低等的零件可以組合成更高等的零件,零件的序號代表了零件的等級,比如5號零件可以由1號和4號零件組合而成,也可以有2號和3號零件組合而成。
現有一個序號為 x 的零件,它是由 n 個序號在[a,b]區間內的零件組合而成,求n 個序號在[a,b]區間內的零件組合為 x 零件的概率


輸入
一行輸入四個整數依次為n,a,b,x,用空格分隔。資料規模和約定
  對於50%的資料,n≤5.
對於100%的資料,n≤100,b≤100.


輸出
輸出一行為組合為 x 零件的概率,小數點後保留四位小數。


樣例輸入
2 1 3 4


樣例輸出
0.3333


  這道題是我在這刷的所有題中最難的一題。是一道概率dp,我活生生做成了數論,最終還是錯了。看了別人的答案.
解題思路
  定義陣列dp,dp[i][j]表示使用i個小零件拼湊標號為j的大零件時,端點在[a,b]內的概率。初始化dp陣列為0,dp[1][j]=1/(b-a+1).dp[i][j]+=dp[1][k]*dp[i-1][j-k];
  程式碼如下:

//動態規劃的解法
//這題目可以取自己本身無限多次
#include<iostream>
#include <iomanip>
using namespace std;

int main()
{
    int i,j;
    float d[52][102]={0};//用於存放之前計算的,d[i][j]表示取i個數,和為j的概率

    int n, a, b, x;
    cin>>n>>a>>b>>x;

    for(j=a;j<=b;j++)
        d[1][j] = 1.0 / (b-a+1);

    for(i=2;i<=n;i++)
        for(j=i*a;j<=i*b;j++)
        {
            for(int k=a;k<=b;k++)
              d[i][j] += d[1][k] * d[i-1][j-k];
        }
   printf("%.4f",d[n][x]);
  // cout<<setprecision(4)<<d[n][x]<<endl;
    return 0;
}

Copyright© by 寒江雪
Date:2017.5.19

相關推薦

動態規劃小結

前言   大概是從5月11號開始在賽碼網上刷題,這個網站上有的題目樣例覆蓋面不全。當然了,不能過於強求,能有這麼個網站練題還是挺好的。練題無論在哪練目的是為了通過題目提升自己.   相比於POJ和HDU等各大知名OJ而言,還是比較簡單了。所以還是建議到POJ上

Const指標小結

Const指標   在c/c++中,指標本身就是一個難點,再加與const的結合,常會產生許多讓人費解的地方,在這裡做個小結. 1.定義const物件 const int buffsize=512; 因為常量定義後就不能修改,所以定義時必須初始化.

非功能性測試

非功能性測試 可用性測試   測試系統對特定使用者組的操作和可用性   通過使用者的使用來評估產品的技術,由於它反映了使用者的真實使用經驗,所以可以視為一種不可或缺的可用性檢驗過程。也就是說,可用性測試是指讓使用者使用產品(服務)的設計原型或者成品,通過

演算法導論——最長公共子串

最長公共子序列問題  C[i][j]表示字串x<1,2,3,…i>與字串y<1,2,3,…j>的最長公共子序列的長度 初始化 C[0][0]=0; 如果x[i]==y[j]       則c[i][j]=c[i-1][j-1]+1; 否則c[i][j

判斷空間中兩直線的位置

判斷空間中兩直線的位置   假設空間中有兩直線l1,l2l1,l2   其中l1=t1a1→+b1l1=t1a1→+b1,l2=t2a2→+b2l2=t2a2→+b2   兩直線在空間中的位置關係

git上優秀的Unity3d開源專案

List of best publicGitHub repositories: — *More to be added.. ** Feel free t

儘可能使用const

使用const的目的是為了指明一個不可被改動的物件,編譯器會強制實施這項約束。        Const與指標之間糾纏不清的關係 lConst int* p;  //p是一個指向const int 的指標,指標無法修改指向的值 lInt* const p;  //p是一個指

金鑰分配與管理

金鑰分配 金鑰分配:角色   在對稱金鑰加密中,雙方在加密之前必須擁有祕密金鑰. 金鑰分配:定義   給互相傳送密文的雙方傳送金鑰,並且不允許讓非法第三方獲得金鑰. 金鑰分配:模式 金鑰由A選擇並親自交給B 可信賴的第三方選擇金鑰後親自交給A和B

Luogu關卡2-15動態規劃的背包問題(2017年10月)

splay image 說明 方案 理解 ostream img 如果 一次 任務說明:這是最基礎的動態規劃。不過如果是第一次接觸會有些難以理解。加油闖過這個坎。 P1060 開心的金明 小明的媽媽給小明N元錢,小明想買m件物品,每個物品價值為 價格*重要度,求出不超過N元

習題詳解動態規劃DP:硬幣遊戲 蛋糕

動態規劃DP硬幣蛋糕塔 硬幣 題目描述 農夫約翰的奶牛喜歡玩硬幣遊戲,因此他發明了一種稱為“Xoinc”的兩人硬幣遊戲。 初始時,一個有N(5 <= N <= 2,000)枚硬幣的堆疊放在地

習題詳解動態規劃:線性DP

最長上升子序列LIS 問題:求一串數字的最長上升自序列 設f[i]為以第i位數字結尾的子序列的最大長度.當我們列舉i時,我們需要從左往右列舉i前面的數字j,若數字j小於數字i則說明數字i可以在以j結尾的序列之後,因此f[j]+1是一種方案數.若沒有比i小的時

USACO題庫 動態規劃 彙總

資料結構能打到省選了=-= 但是其他類問題只能打到普及 普及啊啊啊!!! 而且這邊省選組都是什麼仙人掌啊,什麼系什麼點對啊...感覺資料結構並沒有什麼用 (實際上很有用但我不會運用) 然後頹到提高組來了..結果全是模擬還有一堆沒學的其他玩意 (迴文自動機) 資料結構也沒

BZOJ3162獨釣(樹哈希,動態規劃

const pac string fine bool names 1=1 max 題解 【BZOJ3162】獨釣寒江雪(樹哈希,動態規劃) 題面 BZOJ 題解 忽然翻到這道題目,突然發現就是前幾天一道考試題目。。。 題解: 樹哈希,既然只考慮這一棵樹,那麽,如果兩個點為根

BZOJ3162獨釣 樹同構+DP

eight pri 相同 題解 con oid src str mil 【BZOJ3162】獨釣寒江雪 題解:先進行樹hash,方法是找重心,如果重心有兩個,則新建一個虛點將兩個重心連起來,新點即為新樹的重心。將重心當做根進行hash,hash函數不能太簡單,

洛谷P2858·動態規劃[USACO06FEB]奶牛零食Treats for the Cows

greate single nes 得到 form images include 規劃 ive 題面 題目描述 FJ has purchased N (1 <= N <= 2000) yummy treats for the cows who get money

經典動態規劃

答案 意思 行修改 優化 待修改 最長 長度 u+ mem 五道經典動態規劃問題1)最大子序列和題目描述:一個序列,選和最大的子序列轉移方程:sum[i]=max{sum[i-1]+a[i],a[i]}當前元素的狀態是:自己單獨一組還是並到前面最後的答案max{sum[i]

Foreign動態規劃 [分治][DP]

tex oid ans script 決策 cst pen 因此 out 動態規劃   Time Limit: 50 Sec Memory Limit: 128 MB Description   一開始有n個數,一段區間的價值為這段區間相同的數的對數。  我們想

henuacm2016級暑期訓練動態規劃專題 DWriting Code

set 方案 pen col txt clu 二維 stream bit 【鏈接】 我是鏈接,點我呀:) 【題意】 在這裏輸入題意 【題解】 二維費用背包。 f[i][j][k] 前i個人,寫了j行,bug不超過k的方案數。 可以把每個人看成是一個物品。 它

henuacm2016級暑期訓練動態規劃專題 JRed-Green Towers

cin 使用 space open ble .net long clu 不用 【鏈接】 我是鏈接,點我呀:) 【題意】 在這裏輸入題意 【題解】 顯然最多1000行的樣子。 從上到小做dp 設f[i][j]為前i行,使用了j個紅色方塊的方案數。 f[1][r

LeetCode動態規劃(上篇共75題)

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } 【5】 Longest Palindromic Substring 給一個字串,需要返回最長迴文子串 解法:dp[i][j] 表示 s[i..j] 是否是迴文串,轉移方程是