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;
}














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值