【DP50题】day1

P2285 HNOI2004打鼹鼠

很妙的LIS!!!

朴素DP方法:f[i][j][k]机器人第k时刻在i,j的最大值,but!1e3的n和1e4的m直接炸掉!

范围:1e4的级别->\(O(n^2)\)的算法

集合:发现地鼠的转移随时间递增(根据LIS的思想),能从上个地鼠转移到此时刻的地鼠的条件是在时间间隔内可以移动俩点的曼哈顿距离。

转移

if(abs(x[i]-x[j])+abs(y[i]-y[j])<=t[i]-t[j])
                f[i]=max(f[i],f[j]+1);

终态:记录f[i]的最大值

初始化:f[i]=1

但是此题不可以为了追求O(nlogn)而用 LIS 的优化,因为此题的序列没有传递性

const int N=1e4+10;
int x[N],y[N],t[N];
int f[N];
int n,m,maxx;

#undef int
int main(){
#define int long long
    #ifdef WIN32
    freopen("c.txt","r",stdin);
    #endif
    rd(n),rd(m);
    rep(i,1,m){
        rd(t[i]),rd(x[i]),rd(y[i]);
    }
    rep(i,1,m){
        f[i]=1;
        rep(j,1,i-1){
            if(abs(x[i]-x[j])+abs(y[i]-y[j])<=t[i]-t[j])
                f[i]=max(f[i],f[j]+1);
        }
        maxx=max(maxx,f[i]);
    }
    printf("%lld\n",maxx);
    return 0; 
}

P1336 最佳课题选择

范围:1e2的级别->\(O(n^3)\)的算法

集合:考虑物尽其用,给了m个课题就一个一个课题的去更新,记录前i个课题完成了j篇论文的最小时间。那么转移就可以根据m的阶段来划分,从m的上一个阶段递推。

转移:这道题的转移方程挺容易想的:f[i][j]=min(f[i][j],f[i−1][j−k]+a[i]∗k^b[i])

终态f[m][n]

初始化:根据注意到i-1可能为0,那转移就从这里开始。

const int N=210;
int f[N][N];
int n,m;
int a[N],b[N];

inline int ksm(int a,int k){
    int res=1;
    for(;k;k>>=1){
        if(k&1)res=res*a;
        a=a*a;
    }
    return res;
}

#undef int
int main(){
#define int long long
    #ifdef WIN32
    freopen("c.txt","r",stdin);
    #endif
    rd(n),rd(m);//n:论文数,m:课题数
    rep(i,1,m)rd(a[i]),rd(b[i]);
    rep(i,1,n)f[0][i]=INT_MAX;
    rep(i,1,m)
        rep(j,1,n){
            f[i][j]=INT_MAX;
            rep(k,0,j)
                f[i][j]=min(f[i][j],f[i-1][j-k]+a[i]*ksm(k,b[i]));
        }
    printf("%lld\n",f[m][n]);
    return 0; 
}

P1474 货币系统 Money Systems

完全背包求方案数

不限制每种货币取的个数,则需要使用完全背包,这里是统计方案数,根据加法原理(分步相加)得出转移方程:f[j]=f[j-a[i]].

记得初始化f[0]=1;


P1233 木棍加工

思想:归约:按照第一个关键字从大到小排序后就只用考虑另一个关键字的降序问题;

SOL:

​ 要求的是第二个关键字的最长下降子序列的最小个数,根据导弹拦截第二问用到的定理Dilworth定理:最少的下降序列个数就等于整个序列最长上升子序列的长度,求一遍LIS,因为n<=5e3,我就没用nlogn,\(n^2\)跑一边就好了

const int N=5e3+10;
struct qujian{
    int a,b;
    bool operator <(const qujian &rhs)const{
        if(a!=rhs.a)return a>rhs.a;
        return b>rhs.b;
    }
}mg[N];

int f[N],maxx,n;

#undef int
int main(){
#define int long long
    #ifdef WIN32
    freopen("c.txt","r",stdin);
    #endif
    rd(n);
    rep(i,1,n){
        rd(mg[i].a),rd(mg[i].b);
    }
    sort(mg+1,mg+n+1);
    rep(i,1,n){
        f[i]=1;
        rep(j,1,i-1)
            if(mg[j].b<mg[i].b)
                f[i]=max(f[i],f[j]+1);
        maxx=max(maxx,f[i]);
    }
    printf("%lld\n",maxx);
    return 0;
}

好吧,还是悄咪咪复习一下O(nlogn)的做法

	f[++len]=mg[1].b;
    rep(i,2,n){
        if(mg[i].b>f[len])f[++len]=mg[i].b;
        else{
            int p=lower_bound(f+1,f+len+1,mg[i].b)-f;//求按第二关键字的LIS
            f[p]=mg[i].b;
        }
    }
    printf("%lld\n",len);

P2904 USACO08MAR跨河River Crossing

【前置技能】:语文足够好

【独立思考+1A】

设f[i]表示1~i只牛全部运输过去的最小花费

预处理sum[i]:运i只牛的总花费

转移方程:f[i]=min(f[i],f[i-j]+sum[j]+M)//加M只因为FJ还要回到对岸

记得最后答案再减去一个M,因为最后一次到对岸的时候FJ已经不用再去接Bessie们了

const int N=2510;
int m[N],sum[N],f[N];
int n,M,cnt;

int main(){
    #ifdef WIN32
    freopen("c.txt","r",stdin);
    #endif
    rd(n),rd(M);
    sum[0]=M;
    rep(i,1,n){
        rd(m[i]);
        sum[i]=sum[i-1]+m[i];
    }
    mem(f,0x3f);
    f[0]=0;
    rep(i,1,n){
        rep(j,1,i){
            f[i]=min(f[i],f[i-j]+sum[j]+M);
        }
    }
    printf("%d",f[n]-M);
    return 0;
}
posted @ 2019-10-15 17:44  设计涉及社稷  阅读(126)  评论(0编辑  收藏  举报