1. 程式人生 > >7.29 dp動態規劃

7.29 dp動態規劃

一個 還要 return none class struct one fine 嚴格

技術分享圖片

A題:

Description

為了檢驗你上午有沒有好好聽課,於是又了這一題。給你一個N*M的方格網,左上角為(1,1)右下角為(N, M),每個方格中有一個數a[i][j],剛開始你在位置(1, 1)你每次可以往下走或者往右走一步,你需要確定一種走的方案,最後走到(N, M),使得途徑格子的數的和最大。

Input

輸入的第一行一個整數T(T<= 5)代表測試數據的組數
接下裏T組測試數據
每組測試數據第一行為兩個整數N, M(1 <= N, M <= 1000)代表方格網的大小
接下來N行,每一行M個數,代表a[i][j](1 <= a[i][j] <= 1000)

Output

對於每組測試數據,輸出一個整數代表從(1, 1)走到 (N, M)途徑的格子的最大的和。

Sample Input

1
2 2
100 1
50 1

Sample Output

151


很好列出狀態轉移方程 註意邊界情況
技術分享圖片
#include <bits/stdc++.h>
using namespace std;
int a[1010][1010];
long long dp[1010][1010];
int main()
{
   int t;
   cin>>t;
   while(t--)
   {
       int n,m;
       scanf("%d%d",&n,&m);
       
int i,j; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { scanf("%d",&a[i][j]); } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(i==1&&j==1)dp[i][j]=a[i][j];
else if(i==1)dp[i][j]=a[i][j]+dp[i][j-1]; else if(j==1)dp[i][j]=a[i][j]+dp[i-1][j]; else dp[i][j]=a[i][j]+max(dp[i-1][j],dp[i][j-1]); } } printf("%lld\n",dp[n][m]); } return 0; }
View Code

B題:

Description

averyboy又遇到麻煩了。他的老板給了他一個問題,如果他不能解決,老板將會開除他。老板給的問題如下,給你一個序列a[1]~a[N]和一個數M你需要從這N個數選出M個數組成一個嚴格遞增的序列,問你一共有多少種選法?averyboy不想被開除,你能幫助他嗎?

Input

輸入的第一行為一個整數T(T <= 100)代表測試數據的組數
接下來T組測試數據
每組測試數據第一行為兩個整數N, M(1 <= N <= 1000, 1 <= M <= N)其含義如題目
接下來一行N個整數a[i](1 <= a[i] <= 1e9)

Output

對於每組測試數據輸出一個整數代表從N個數中選出M個數組成嚴格遞增序列的選法。因為答案可能很大,所以最後你需要對1e9 + 7取模後輸出

Sample Input

2
3 2
1 2 3
3 2
3 2 1

Sample Output

3
0

HINT

第一組測試數據可以選1,2或者,1,3或者2,3一共三種選法

此題很難,據說是CCPC原題,要用到動態規劃,樹狀數組,離散化

樹狀數組求逆序對的思想,把值當位置,可求出第i個元素前面比a[i]小的元素個數,在求和上把復雜度從n降到logn

因為a[i]值很大,開不了那麽大的數組,所以離散化用相對大小代表值

技術分享圖片
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn=1010;
const int mod=1e9+7;
LL tree[maxn][maxn];//樹狀數組,代表區間和
LL dp[maxn][maxn];//dp[i][j]表示考慮到第i個數,且以a[i]結尾,長度為j的遞增序列個數
int a[maxn];
int n,m;
struct node
{
    int value;
    int id;
    bool operator<(const node &res)const
    {
        if(value==res.value)return id>res.id;//把值相同的編號按從大到小排,編號小的放到後面,這樣算前綴和的時候就不會把值相等編號又比它小的算進去
        else return value<res.value;//按值從小到大排序
        //例如 5 5 8 ,算第二個5的時候就不會把第一個5算進去,算第一個5的時候後面那個5還沒考慮進來呢
    }
}Node[maxn];
int Rank[maxn];//離散化後表示相對位置大小的數組(第一小,第二小...)把值的大小轉化為相對位置的大小
int lowbit(int x)
{
    return x&(-x);
}
void update(int loc,int d,int value)
{
    for(int i=loc;i<=n;i+=lowbit(i))
    {
        tree[i][d]=(tree[i][d]+value)%mod;//更新操作可以這樣理解 將loc位置上的值為0元素改成了value,則包含這個位置的所有區間和tree[i]都要加上value
    }                                                  //關於哪個區間含有這個元素有詳細的證明
}
LL query(int loc,int d)
{
    LL ans=0;
    for(int i=loc;i>=1;i-=lowbit(i))//查詢操作是查詢從1到loc的前綴區間和
    {
        ans=(tree[i][d]+ans)%mod;
    }
    return ans;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%d%d",&n,&m);
    int i,j;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&Node[i].value);
        Node[i].id=i;
    }
    sort(Node+1,Node+n+1);//將元素按從小到大排好序
    for(i=1;i<=n;i++)//離散化
    {
        Rank[Node[i].id]=i;   //Node已經排好序了,i即代表Node的相對位置大小
    } //例如,第一個元素,它在未排序前的編號為Node[1].id,輸入編號,即可知道它現在的值(相對位置大小,可代表值)
    for(i=1;i<=n;i++)  //以a[i]為終點。考慮到第i個數
    {
        dp[i][1]=1;//終點為i,長度為1的肯定只有它本身這一個啊
        update(Rank[i],1,1);//把長度為1且個數為1的更新進去
        for(j=2;j<=min(m,i);j++)//從長度為2的開始遍歷,考慮i之前的數,長度肯定小於i,並且只考慮到長度為m就夠了
        {
            LL temp=query(Rank[i]-1,j-1);//以剛好比a[i]小的數結尾,長度為j-1的遞增序列有多少個
            dp[i][j]=(dp[i][j]+temp)%mod;
            update(Rank[i],j,dp[i][j]);
        }
    }
    LL ans=0;
    for(i=1;i<=n;i++)
    {
        ans=(ans+dp[i][m])%mod;
    }
    printf("%lld\n",ans);
    memset(dp,0,sizeof dp);
    memset(tree,0,sizeof tree);
    }
return 0;
}
View Code

C題:

Description

不僅天外天喜歡子區間,averyboy也非常喜歡子區間。現在天外天給averyboy一個長度為N的序列a[1]~a[N],天外天讓averyboy找出一個子區間[l, r]使得這個子區間數的和要比其他子區間數的和要大

Input

第一行一個整數T(T <= 10)代表測試數據的組數
接下來T組測試數據
每組測試數據第一行為一個整數N(1 <= N <= 1e5)代表序列的長度
接下來一行N個整數a[i](-1000 <= a[i] <= 1000)代表序列a[i]

Output

對於每組測試數據,輸出一個整數,代表最大的子區間和。

Sample Input

2
3
1 -100 3
4
99 -100 98 2

Sample Output

3
100

HINT

第一組測試樣例,選擇區間[3,3]和為3最大,第二組測試樣例選擇區間[3, 4]和為98 + 2 = 100最大

此題兩種解法,可以用線段樹,也可以dp

線段樹求區間最大連續和

技術分享圖片
#include <bits/stdc++.h>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int a[maxn];
struct node
{
    int L,R;
    long long ms,rs,ls,s;
}Node[maxn<<2];
void pushup(int i)
{
   Node[i].ms=max(Node[i<<1].ms,Node[(i<<1)|1].ms);
   Node[i].ms=max(Node[i].ms,Node[i<<1].rs+Node[(i<<1)|1].ls);
   Node[i].ls=max(Node[i<<1].ls,Node[i<<1].s+Node[(i<<1)|1].ls);
   Node[i].rs=max(Node[(i<<1)|1].rs,Node[i<<1].rs+Node[(i<<1)|1].s);
   Node[i].s=Node[i<<1].s+Node[(i<<1)|1].s;
   return;
}
void build(int i,int l,int r)
{
    Node[i].L=l;
    Node[i].R=r;
    if(r==l)
    {
        Node[i].s=a[l];
        Node[i].ms=a[l];
        Node[i].ls=a[l];
        Node[i].rs=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    pushup(i);
}
long long queryR(int i,int l,int r)
{
    if(Node[i].L==l&&Node[i].R==r)
    {
        return Node[i].rs;
    }
    int mid=(Node[i].L+Node[i].R)>>1;
    if(r<=mid) return queryR(i<<1,l,r);
    else if(l>mid) return queryR((i<<1)|1,l,r);
    else {
            long long lans=queryR(i<<l,l,mid);
    long long rans=queryR((i<<1)|1,mid+1,r);
    return max(rans,lans+Node[(i<<1)|1].s);
    }
}
long long queryL(int i,int l,int r)
{
    if(Node[i].L==l&&Node[i].R==r)
    {
        return Node[i].ls;
    }
    int mid=(Node[i].L+Node[i].R)>>1;
    if(r<=mid) return queryL(i<<1,l,r);
    else if(l>mid) return queryL((i<<1)|1,l,r);
    else{
            long long lans=queryL(i<<l,l,mid);
    long long rans=queryL((i<<1)|1,mid+1,r);
    return max(lans,rans+Node[i<<1].s);
    }
}
long long query(int i,int l,int r)
{
    if(Node[i].L==l&&Node[i].R==r)
    {
        return Node[i].ms;
    }
    int mid=(Node[i].L+Node[i].R)>>1;
    if(r<=mid) return query(i<<1,l,r);
    else if(l>mid) return query((i<<1)|1,l,r);
    else
    {
        long long  lans=query(i<<1,l,mid);
    long long rans=query((i<<1)|1,mid+1,r);
    long long ans=max(lans,rans);
    return max(ans,queryR(i<<1,l,mid)+queryL((i<<1)|1,mid+1,r));
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        scanf("%d",&n);
        int i;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        build(1,1,n);
        long long ans=query(1,1,n);
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

dp

技術分享圖片
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000 + 10;
typedef long long LL;
const LL mod = 1e9 + 7;
int N, M;
int a[maxn];
LL Tree[maxn][maxn];
LL dp[maxn][maxn];//dp[i][j]表示考慮到第i個數,且以第a[i]個數結尾,長度為j的遞增序列個數
struct node{
    int value;
    int id;
    bool operator <(const node &res) const{
        if(value == res.value) return id > res.id;
        else return value < res.value;
    }
}Node[maxn];
int Rank[maxn];
void init()
{
    memset(Tree, 0, sizeof(Tree));
    memset(dp, 0, sizeof(dp));
}
int lowbit(int x)
{
    return x&(-x);
}
void add(int loc, int d, LL value)
{
    for(int i = loc; i <= N; i += lowbit(i))
    {
        Tree[i][d] = (Tree[i][d] + value) % mod;
    }
}
LL get(int loc, int d)
{
    LL ans = 0;
    for(int i = loc; i >= 1; i -= lowbit(i))
    {
        ans = (ans + Tree[i][d]) % mod;
    }
    return ans;
}
int main()
{
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &N, &M);
        init();
        for(int i = 1; i <= N; i++)
        {
            scanf("%d", &Node[i].value);
            Node[i].id = i;
        }
        sort(Node + 1, Node + N + 1);
        for(int i = 1; i <= N; i++)
        {
            Rank[Node[i].id] = i;
        }
        for(int i = 1; i <= N; i++)
        {
            dp[i][1] = 1;
            add(Rank[i], 1, 1);
            for(int j = 2; j <= min(M, i); j++)
            {
                LL temp = get(Rank[i] - 1, j - 1);
                dp[i][j] = (dp[i][j] + temp) % mod;
                add(Rank[i], j, dp[i][j]);
            }
        }
        LL ans = 0;
        for(int i = 1; i <= N; i++)
        {
            ans = (ans + dp[i][M]) % mod;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
View Code

D題:

averyboy家有一棵蘋果樹。把這棵蘋果樹看成一個由N(編號為1~N)個節點組成的以1號節點為根的有根樹。每個節點上有一個蘋果,每個蘋果也有一個營養價值a[i]。現在averyboy想知道以每個節點為根的子樹上營養價值為奇數的節點的個數。

Input

輸入第一行為一個整數T(T <= 5)代表測試數據的組數
接下來T組測試數據
每組測試數據第一行為一個整數N(1 <= N <= 1e5)
接下來一行N個非負整數a[i]代表每一個節點上的一個蘋果的營養價值(0 <= a[i] <= 1e6)
接下來N - 1行,每一行兩個整數u, v代表u, v之間有一條邊(1 <= u, v <= N)

Output

對於每組測試數據,輸出一行N個數,第i個數代表以第i節點為根的子樹(子樹包括自己)上蘋果營養價值為奇數的個數

Sample Input

2
3
1 2 3
1 2
2 3
3
1 1 1
1 2
2 3

Sample Output

2 1 1
3 2 1

HINT

在第一組樣例中,以1為根的子樹包括節點1,2,3但是由於2號節點上的蘋果營養價值為2不是奇數,所以以1為根的子樹上一共有2個營養價值為奇數的蘋果。以2為根的子樹包括節點2, 3,所以只有1個營養價值為奇數的蘋果.以3為根的子樹就是3自身,所以也只有1個營養價值為奇數的蘋果。所以最後輸出2 1 1

此題就是樹狀dp,用前向星存圖即可

技術分享圖片
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;//註意這裏的maxn應該開題目給的雙倍,因為邊是雙向的,加邊還要開更多
int a[maxn];
int dp[maxn];
bool visit[maxn];
int n;
int head[maxn];  //head[i]表示以i為起點的最後一條邊的編號;
struct edge
{
    int to;//這條邊的終點
    int last;  //與自己起點相同的上一條邊的編號
}Edge[maxn*2];//邊數組
int cnt; //記錄當前邊的編號
void add(int u,int v)//加邊    //起點u,終點v;;
{
    Edge[cnt].to=v;
    Edge[cnt].last=head[u];//現在是要把編號為cnt的邊加進來,
    head[u]=cnt++;//現在cnt邊加進來了,cnt邊為以u為起點的最後一條邊
}
void dfs(int root)//這棵樹的根節點
{
    if(a[root]&1)dp[root]=1;//根節點自己
    else dp[root]=0;
    visit[root]=true;  //節點已訪問過
    for(int i=head[root];i!=-1;i=Edge[i].last)//把節點當起點,
    {
        int v=Edge[i].to;//子節點是終點
        if(!visit[v])
        {
            dfs(v);//把子節點當根節點去找它的子節點
            dp[root]+=dp[v];//根節點的子節點數加上它的子節點的子節點數
        }
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%d",&n);
        int i;
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        for(i=1;i<=n;i++)head[i]=-1;
        cnt=1;
        for(i=1;i<=n-1;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        memset(visit,false,sizeof visit);//記得一定要memset
        dfs(1);
        for(i=1;i<=n;i++)printf("%d ",dp[i]);
        printf("\n");
    }
    return 0;
}
View Code



7.29 dp動態規劃