2017 暑假艾教集訓 day3


題目:https://cn.vjudge.net/contest/176068#overview


Uva 12260
做法:Petra的策略滿足貪心,所以先把糖果按P在按J排序,然後去取,就看Jan會取哪些糖果了。
 每次默認Jan先取,如果petra先取的話,i從2開始循環。
那麼每個糖果就可以看成是Jan取不取,但是要注意,由於Petra是一定會往後一個取,所以Jan取糖果的時候是有一定的限制的,該限制爲:假如都是Jan先取,1個糖果,Jan可以拿到,2個糖果,Jan可以拿其中一個,3個糖果Jan可以拿其中兩個,4個糖果也是其中兩個,以此類推,Jan能拿的糖果數爲(i + 1)/2。因此dp[i][j]表示Jan在前i個糖果中拿了j個,j <= (i + 1)/2
#include <bits/stdc++.h>
using namespace std;
struct node
{
    int va,vb;
}val[1100];
bool cmp(const struct node a,const struct node b)
{
    if(a.va!=b.va) return a.va>b.va;
    return a.vb < b.vb;
}
char s[30];
int dp[1100][1100];
int cost[1100][1100];
int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        scanf("%s",s);
        int sum=0;
        for(int i=1;i<=n;++i)
        {
            scanf("%d%d",&val[i].va,&val[i].vb);
            sum+=val[i].va;
        }
        sort(val+1,val+1+n,cmp);
        memset(dp,0,sizeof(dp));
        memset(cost,0,sizeof(cost));
        int cnt=0;
        for(int i=(s[0]=='P')? 2:1 ;i<=n;++i)
        {
            ++cnt;
            for(int j=1;j<=(cnt+1)/2;++j)
            {
                dp[i][j] = dp[i-1][j];
                cost[i][j] = cost[i-1][j];

                if(dp[i-1][j-1] + val[i].vb > dp[i][j])
                {
                    dp[i][j] = dp[i-1][j-1] + val[i].vb;
                    cost[i][j] = cost[i-1][j-1] + val[i].va;
                }
                else if(dp[i-1][j-1] + val[i].vb == dp[i][j])
                {
                    cost[i][j] = min(cost[i][j],cost[i-1][j-1]+val[i].va);
                }
            }
        }
        printf("%d %d\n",sum-cost[n][(cnt+1)/2],dp[n][(cnt+1)/2]);
    }
    return 0;
}


HDU 4662
做法: 把U都變成I 統計有多少個I 。  如果滿足 2^K - num = 6 * T ; 那麼就是Yes,只需要枚舉比num大 兩次即可


Uva 1362
做法:dp[i][j]表示從i到j的位置可以用多少種多叉樹表示。轉移方程:dp[i][j]=∑(k=i+2->j)dp[i+1][k−1]∗dp[k][j]。
相當於每次枚舉一個子樹的位置即可,因爲要回溯,所以要枚舉s[i]=s[k]

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod = 1000000000;
ll dp[320][320];
char s[310];

ll cal(int l,int r)
{
    if(dp[l][r]!=-1)    return dp[l][r];
    if(l==r)            return 1;
    if(s[l] != s[r])    return 0;

    dp[l][r]=0;
    for(int i=l+2;i<=r;++i)
    {
        if(s[l]==s[i] && s[r]==s[i])
        dp[l][r] = (dp[l][r] + (ll)cal(l+1,i-1)*(ll)cal(i,r))%mod;
    }
    return dp[l][r]%mod;
}
int main()
{
    freopen("exploring.in","r",stdin);
    freopen("exploring.out","w",stdout);

    while(scanf("%s",s)!=EOF)
    {
        memset(dp,-1,sizeof(dp));
        printf("%I64d\n",cal(0,strlen(s)-1));
    }
    return 0;
}

CF 678C
做法: 設dp[i][j] , 意爲 i->j 是否可取。 然後做01揹包。 不難發現 如果 dp[i][j]可取 ,那麼dp[i+a[k]][j] dp[i+a[k]][j+a[k]]
也都可以取。
for(int i=1;i<=n;++i)
    {
        for(int k=aim;k>=a[i];--k)
        {
            for(int j=0;j+a[i]<=aim;++j)
            {
                if(dp[k-a[i]][j])
                {
                     dp[k][j]=dp[k][j+a[i]]=1;
                }
            }
        }
    }

HDU 4960
做法:首先要預處理出所有關鍵點(i,j) 滿足 sum[1->n] == sum[j->n]。然後 做狀態dp[]的一維狀態轉移即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[510000];
int n;
int a[5100];
int v[5100];
int qsum[5100];
int hsum[5100];
struct node
{
    int a,b;
}book[510000];
bool cmp(const node x,const node y)
{
    if(x.a != y.a) return x.a < y.a;
    return x.b>y.b;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        for(int i=1;i<=n;++i) scanf("%d",&a[i]);
        for(int i=1;i<=n;++i) scanf("%d",&v[i]);
        qsum[0]=0;
        for(int i=1;i<=n;++i) qsum[i] = qsum[i-1] + a[i];

        hsum[n+1]=0;
        for(int i=n;i>=1;--i) hsum[i] = hsum[i+1] +a[i];

        int cnt=1;
        book[0].a=0; book[0].b=n+1;

        for(int i=1;i<=n;++i)
        {
            for(int j=i+1;j<=n;++j)
            {
                if(qsum[i]==hsum[j])
                {
                    book[cnt].a = i;
                    book[cnt++].b = j;
                }
            }
        }
        sort(book,book+cnt,cmp);
        ll ans=v[n];
        for(int i=0;i<cnt;++i) dp[i]=0x3f3f3f3f3f3f;
        dp[0]=0;
        for(int i=1;i<cnt;++i)
        {
            for(int j=i-1;j>=0;j--)
            {
                int l=book[i].a-book[j].a;
                int r=book[j].b-book[i].b;
                dp[i] = min(dp[i],dp[j]+v[l]+v[r]);
            }
            ans= min( ans, dp[i] + v[ book[i].b-book[i].a-1]);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

BZOJ 4806
做法:dp[i][j] 意爲在m列中還有多少列有0個炮,有1個炮 的方案數。每次按行轉移。有一種不放的情況,兩種放一個炮的情況,三种放兩個炮的情況。慢慢轉移就行了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=999983;
int n,m;
ll dp[110][110][110];
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(dp,0,sizeof(dp));

        dp[0][m][0]=1;
        int ans=0;

        for(int t=1;t<=n;++t)
        {
            for(int i=0;i<=m;++i)
            {
                for(int j=0;j<=m;++j)
                {
                    dp[t][i][j]=dp[t-1][i][j]%mod;

                    dp[t][i][j]=(dp[t][i][j] +((j+1)*dp[t-1][i][j+1])%mod)%mod;
                    if(j>=1) dp[t][i][j]=(dp[t][i][j] +((i+1)*dp[t-1][i+1][j-1])%mod)%mod;

                    dp[t][i][j]=(dp[t][i][j] +((j+2)*(j+1)*dp[t-1][i][j+2]/2) %mod )%mod;
                    dp[t][i][j]=(dp[t][i][j] +((i+1)*(j)*dp[t-1][i+1][j]) %mod)%mod;

                    if(j>=2) dp[t][i][j]=(dp[t][i][j] +((i+2)*(i+1)*dp[t-1][i+2][j-2]/2) %mod  )%mod;

                    if(t==n) ans=(ans+dp[t][i][j])%mod;
                }
            }
        }
        printf("%lld\n",ans%mod);
    }
    return 0;
}

HDU 5543
做法:比普通的01揹包多加了一維狀態,意爲當前兩邊放了幾個。轉移的時候 三種形式放置,一種放在邊上,一種放在普通位置,一種不放
注意:這裏把 L 和每個棒的長度都乘以2 ,因爲 左右兩邊只需要放一半的長度就可以放下(避免奇數)
同時這道題有個BUG ,當只有一個木棍的時候,怎麼都能放下。(要特判),01揹包滾動數組時候,要逆向遍歷當前揹包的大小

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,l;
ll dp[2005*2][4];

int len[1005];
ll val[1005];
int main()
{
    int T;
    int kiss=0;
    scanf("%d",&T);
    while(T--)
    {
         scanf("%d%d",&n,&l);
         l=l*2;
         for(int i=1;i<=n;++i)
         {
             scanf("%d %I64d",&len[i],&val[i]);
             len[i]= len[i]*2;
         }
         if(n==1)
         {
            printf("Case #%d: %I64d\n",++kiss,val[1]);
            continue;
         }

         memset(dp,0,sizeof(dp));
         for(int i=1;i<=n;++i)
         {
            for(int j=l;j>=(len[i]/2);--j)
            {
                for(int k=0;k<=2;++k)
                {
                    if(k<=1) dp[j][k] = max(dp[j][k],dp[j-(len[i]/2)][k+1]+val[i]);
                    if(j>=len[i]) dp[j][k] = max(dp[j][k],dp[j-len[i]][k]+val[i]);
                }
            }
         }

         ll ans=0;
         for(int j=0;j<=l;++j)
         {
             for(int k=0;k<=2;++k)
             {
                 ans=max(dp[j][k],ans);
             }
         }
         printf("Case #%d: %I64d\n",++kiss,ans);
    }
    return 0;
}














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章