SEON - 1 怒怼Dp(1)

关于标题

(S)Serious(E)Exercise(O)Of(N)Noip


怒怼Dp系列

T1

题目背景:

HDU 1158 Employment Planning

解题报告:

…一个比较简单且暴力的Dp题。。
先求出最多需要的工人数量减少后面枚举的次数。。
然后根据时间Dp就行了。。。

状态转移方程:

f[i][j] = min(f[i][j],f[i-1][k]+jud(k,j));
//i:天数; 
//j:当天所用员工数; 
//k:昨天所用员工数

int jud(int pre,int now)
{
    if(pre>now)return (pre-now)*fire+now*fiee;
    else       return (now-pre)*hire+now*fiee;  
}

my.cpp

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;

inline int read()
{
    int w=1,x=0;char ch;
    while(ch< '0'||ch >'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*w;
}

int n,hire,fiee,fire,maxx;
int c[20],f[20][1000];

int jud(int pre,int now)
{
    if(pre>now)return (pre-now)*fire+now*fiee;
    else       return (now-pre)*hire+now*fiee;  
}

int main()
{       
    while((n=read())&&n!=0)
    {
        memset(f,63,sizeof(f));maxx=0;
        hire=read();
        fiee=read();
        fire=read();
        for(int i=1;i<=n;i++)cin>>c[i],maxx=max(maxx,c[i]);
        f[0][0] = 0;
        for(int i=1;i<=n;i++)
          for(int j=c[i];j<=maxx;j++)
            for(int k=0;k<=maxx;k++)
             f[i][j] = min(f[i][j],f[i-1][k]+jud(k,j));

        int ans=99999999;
        for(int i=0;i<=maxx;i++)ans=min(ans,f[n][i]);
        cout << ans << endl;
    }
}

T2

题目背景:

HDU 1078 FatMouse and Cheese

解题报告:

注意这里老鼠只能到达在他所在位置的水平方向或竖直方向的洞穴前。

状态转移方程:

f[x][y] = dfs(xi,yi) + c[x][y];
//x,y:横,纵坐标。
//c[x][y]洞穴中奶酪的数目。
//边界:(xi<=n&&xi>=1&&yi<=n&&yi>=1)(c[xi][yi]>c[x][y])

my.cpp

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;

inline int read()
{
    int w=1,x=0;char ch;
    while(ch< '0'||ch >'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*w;
}

int n,k,ans=0;
int c[105][105];
int f[105][105];
int dir[4]={0,0,1,-1};

int dfs(int x,int y)
{
    int maxx=0;
    if(!f[x][y])
    {
        for(int t=1;t<=k;t++)
        {
            for(int i=0;i<4;i++)
            {
                int xi = x+dir[i]*t;
                int yi = y+dir[3-i]*t;
                if(xi<=n&&xi>=1&&yi<=n&&yi>=1)
                if(c[xi][yi]>c[x][y])
                maxx = max(maxx,dfs(xi,yi));
            }
        }
        f[x][y] = maxx+c[x][y];
    }
    return f[x][y];
}

int main()
{       
    while((n=read(),k=read())&&(n!=-1&&k!=-1))
    {
        memset(f,0,sizeof(f));
        memset(c,0,sizeof(c));
        ans=0;
        for(int i=1;i<=n;i++)
          for(int j=1;j<=n;j++)
            c[i][j] = read();
        ans = max(ans,dfs(1,1));
        cout << ans << endl;        
    }
}

T3

题目背景:

HDU 1160 Fat Mouse`s Speed

解题报告:

网上说是一道最长不下降序列的模板题。。。
然而我做了1个多小时orz

状态转移方程(经典):

pre[i]=((f[i]>=f[j]+1)?(pre[i]):(j));
//记录比它大的数,放在转移方程之前好比较。

f[i]=((f[i]>=f[j]+1)?(f[i]):(f[j]+1));
//i:前一个字符串正在比较的字符的下标
//j:后一个字符串正在比较的字符的下标

if(f[i]>ans1)ans1 = f[i],tot=i; 
//记录答案的下标
tot=pre[tot];
//用链表(?)输出答案

my.cpp

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;

inline int read()
{
    int w=1,x=0;char ch;
    while(ch< '0'||ch >'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*w;
}

struct node{int pos,wgt,spd;}mce[100050];

bool comp1(const node &a,const node &b)
{return a.wgt>=b.wgt;}

int cnt=1,tot=0;
int f[100050],pre[100050],ans[100050];
int main()
{   
    //freopen("hh.in","r",stdin);
    while((scanf("%d%d",&mce[cnt].wgt,&mce[cnt].spd))!=EOF)
        {mce[cnt].pos = cnt;f[cnt++]=1;}
    cnt-=1;
    sort(mce+1,mce+cnt+1,comp1);

    int ans1=-1,st=0;
    for(int i=1;i<=cnt;i++)
    {
        for(int j=1;j<i;j++)
            if(mce[i].spd>mce[j].spd&&mce[i].wgt<mce[j].wgt)
            {
                pre[i] = ((f[i]>=f[j]+1)?(pre[i]):(j));
                f[i] = ((f[i]>=f[j]+1)?(f[i]):(f[j]+1));            
            }

        if(f[i]>ans1)ans1 = f[i],tot=i;     
    }
    cout << ans1 << endl;
    cnt=0;
    while(tot){cout<<mce[tot].pos<<endl;tot=pre[tot];}

}

T4

题目背景:

HDU 1024 Max Sum Plus Plus

解题报告:

状态转移方程

for(int i=1;i<=m;i++)
{       
    int cas=0;
    for(int j=1;j<=i;j++)cas += a[j];
}
//预处理

cas = max(premax[j-1],cas)+a[j];
premax[j-1] = premax[n];
premax[n] = max(premax[n],cas);
//i:区间起点
//j:以该下标为终点的最大区间和

附带标准最大区间和O(1)的转移方程与友情链接:

//基于数组的:maxn[i]=max{0,maxn[i-1]}+a[i]
for(int i=1;i<=n;i++)
{
    last = max(0,last)+a[i];
    ans = max(ans,last);
}
//基于前缀和的:ans=max{sum[i]-min{sum[j]} | 0<=j<i<=n }
for(int i=1;i<=n;i++)
{
    ans = max(ans,sum[i]-minn);
    minn = min(minn,sum[i]);
}

详细讲解的友情链接

my.cpp

//给定由 n个整数(可能为负整数)组成的序列a1,a2,a3,……,an,以及一个正整数 m,要求确定序列 a1,a2,a3,……,an的 m个不相交子段,
//使这m个子段的总和达到最大,求出最大和。
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;

inline int read()
{
    int w=1,x=0;char ch;
    while(ch< '0'||ch >'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*w;
}

int a[1000050];
int premax[1000050];
int m,n;

int dp(int n,int m)
{
    memset(premax,0,sizeof(premax));
    for(int i=1;i<=m;i++)
    {
        int cas=0;
        for(int j=1;j<=i;j++)cas += a[j];
        premax[n] = cas;
        for(int j=i+1;j<=n;j++)
        {
            cas = max(premax[j-1],cas)+a[j];
            premax[j-1] = premax[n];
            premax[n] = max(premax[n],cas);
        }
    }
    return premax[n];
}


int main()
{
    while(cin>>m>>n)
    {
        for(int i=1;i<=n;i++)cin>>a[i],premax[i]=0;
        cout << dp(n,m) << endl;
    }
}

总结

果然多刷题效果就好很多。
接下来每天除了应付测试再做两道Dp,顺便在背一个模板。
(妈的我要在考试前背完21个模板和对应的样例还要在复赛考试前打到计算机上。。。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值