动态规划测试test20170430

前言

这次考试据说有点难度,果然有些题目的确看着有些头晕,不过整体而言还算好吧。

题目

1.我不想写背景(wtf.pas/c/cpp)

【题目描述】

某巨魔去滑雪(没滑雪板),但他的技术并不精湛,在滑雪场里,每天会提供S门滑雪课。第i节课始于时间Mi,上课的时长为Li(只有在Mi时刻才能选择去上第i节课,其他时间不能选择上第i节课)。上完第i节课后,巨魔的滑雪能力会变成Ai. (注意:这个能力是绝对的,不是能力的增长值)。 巨魔买了一张地图,地图上显示了N个可供滑雪的斜坡,从第i个斜坡的顶端滑至底部所需的时长Di,以及每个斜坡所需要的滑雪能力Ci,以保证滑雪的安全性。巨魔的能力必须大于等于这个等级,以使得他能够安全滑下。 巨魔可以用他的时间来滑雪,上课,或者在旁边菊花丛中练习箭法,但是他必须在T时刻离开滑雪场。这意味着他必须在T时刻之前(或者T时刻)完成最后一次滑雪(或者上课)。 求巨魔在时间内最多可以完成多少次滑雪。这一天开始的时候,他的滑雪能力为1。

【输入格式】

第一行3个数字,T、S、N。
接下来S行,每行3个数字Mi、Li、Ai。
接下来N行,每行2个数字Ci、Di。

【输出格式】

一个整数,表示巨魔滑雪的最大次数。

【样例输入输出】

wtf.in
10 1 2
3 2 5
4 1
1 3
wtf.out
6

【样例解释】

0时刻,选择在第1个斜坡上滑雪,时间花费3。
3时刻,选择上第1节课。滑雪技术提高到5,时间花费2。
5时刻,选择在第2个斜坡上滑雪,时间花费1。
6时刻,选择在第2个斜坡上滑雪,时间花费1。
7时刻,选择在第2个斜坡上滑雪,时间花费1。
8时刻,选择在第2个斜坡上滑雪,时间花费1。
9时刻,选择在第2个斜坡上滑雪,时间花费1。
10时刻,收队了。
总滑雪次数:6

【数据范围】

50%的数据:1N,T1000
100%的数据:1N,T100001S,Ai,Ci1001Mi,Li,Di10000

【题解】

原题在此:P2948 [USACO09OPEN]滑雪课Ski Lessons
我们可以直接令f(t,k) 表示当前时间为 t,技能为k 还能滑的最多次数,状态转移:
f(t.k)=max{f(t+1,k)max{f(Mi+Li)(1is,t=Mi)}max{f(t+Di,k)(1in,kci)}

然后发现还是会超时,所以就要进行优化操作,我们可以发现提前把第i个培训记录到t=Mi 处,状态转移只需要访问对应时间处的链表。 预处理随着技能k 的变化,滑一次需要的最短时间.

所以我们可以得到

f(t.k)=max{f(t+1,k)max{f(Mi+Li)(1is,t=Mi)}1+f(t+Pk,k)

然后就不会超时了。

【代码】

#include <cstdio>
#include <cstring>
#include <algorithm>

#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))

inline int read() {
    int in=0,f=1;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())
        if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar ())
        in=in*10+ch-'0';
    return in*f;
}

const int maxn = 10000+10;
const int maxs = 100+10;

struct Cr {
    int x,y,lv;
}cource[maxs];

struct Node {
    int lv,w;
}node[maxn];

int t,n,s,Lv;
int f[maxn],w[maxn],lv[maxn];
int ff[maxs][maxn];
int rec[maxs][2];

namespace myself {

    bool cmp(const Node &a,const Node &b) {
        return a.lv<b.lv;
    }

    bool cmp2(const Cr &a,const Cr &b) {
        return a.x<b.x;
    }

    inline void pre() {
        std::sort(node+1,node+n+1,cmp);
        rec[node[1].lv][0]=1;
        for(int i=1;i<=n;i++) {
            while(node[i].lv==node[i+1].lv) {
                i++;
            }
            rec[node[i].lv][1]=i;rec[node[i+1].lv][0]=i+1;
        }
    }

    inline void work1() {
        for(int k=1;k<=Lv;k++) {
            if(rec[k][0]!=rec[k][1] or rec[k][0]) {
                for(int i=rec[k][0];i<=rec[k][1];i++)
                    for(int j=node[i].w;j<=t;j++)
                        f[j]=Max(f[j],f[j-node[i].w]+1);
            }
            for(int i=1;i<=t;i++)
                ff[k][i]=f[i];
        }
    }

    inline void work2() {
        work1();
        memset(f,0,sizeof f);
        std::sort(cource+1,cource+s+1,cmp2);
        cource[s+1].x=t;cource[0].y=0;cource[0].lv=1;
        for(int i=s;i>=0;i--) {
            for(int j=i+1;j<=s+1;j++) {
                if(cource[j].x-cource[i].x-cource[i].y>=0)
                    f[i]=Max(f[i],f[j]+ff[cource[i].lv][cource[j].x-cource[i].x-cource[i].y]);
            }
        }
        printf("%d\n",f[0]);
    }

}

int main() {
    freopen("wtf.in","r",stdin);
    freopen("wtf.out","w",stdout);
    t=read();s=read();n=read();
    for(int i=1;i<=s;i++) {
        cource[i].x=read();cource[i].y=read();
        cource[i].lv=read();Lv=Max(Lv,cource[i].lv);
    }
    for(int i=1;i<=n;i++) {
        node[i].lv=read();node[i].w=read();
    }
    myself::pre();
    myself::work2();
    return 0;
}

2.我真不想写背景(wth.pas/c/cpp)

【题目描述】

某巨魔突然对等式很感兴趣,他正在研究 a1x1+a2x2+…+anxn=B 存在非负整
数解的条件,他要求你编写一个程序,给定 N、{an}、以及 B 的取值范围,求出
有多少 B 可以使等式存在非负整数解。

【输入格式】

输入的第一行包含 3 个正整数,分别表示 N、BMin、BMax 分别表示数列的
长度、B 的下界、B 的上界。
输入的第二行包含 N 个整数,即数列{an}的值。

【输出格式】

输出一个整数,表示有多少 B 可以使等式存在非负整数解。

【样例输入输出】

wth.in
2 5 10
3 5
wth.out
5

【样例解释】

对于 B=5,式子有 x1=0,x2=1。
对于 B=6,式子有 x1=2,x2=0。
对于 B=7,无解。
对于 B=8,式子有 x1=1,x2=1。
对于 B=9,式子有 x1=3,x2=0。
对于 B=10,式子有 x1=0,x2=2。

【数据范围】

20%的数据,N51BMinBMax10
40%的数据,N101BMinBMax106
100%的数据,N12 ,0ai41051BMinBMax1012

【题解】

原题:bzoj2118 墨墨的等式
40分:
由于b的数量较小,这就和我们平时使用的动态规划算法没有什么区别,这就是的完全揹包问题,动态规划方程:F[i,j]=F[i1,jai]orF[i,j]
100分:
找一个ai,若x为合法的B,则x+ai也合法
设bi为最小的x,满足x mod mn = i
求出每个bi就可以求答案了
bi用最短路求就好了啊 意会一下
最后枚举余数搞一下就算出答案了

详细一点的解释可以看LCY大神的blog

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=500002;
inline LL read(){
    char ch=getchar(); LL x=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0'; ch=getchar();}
    return x;
}
struct g{
    LL w;int x;
    bool operator <(g a)const{return w>a.w;}
};
struct u{int y;LL c;int next;}a[12*N]; int len,first[N];
void ins(int x,int y,LL c){a[++len]=(u){y,c,first[x]},first[x]=len;}
int o[13]; LL d[N];
priority_queue<g>q;
int main()
{
    int n=read(),i,j,mn=1e6; LL b1=read(),b2=read();
    for(i=1;i<=n;i++)o[i]=read(),mn=o[i]<mn?o[i]:mn;
    for(i=1;i<mn;i++)d[i]=1e15;
    for(i=1;i<=n;i++)
        if(o[i]%mn){
            int _=o[i]%mn;
            for(j=0;j<mn;j++)ins(j,(j+_)%mn,o[i]);
        }
    q.push((g){0,0});
    while(!q.empty()){
        g r=q.top(); q.pop(); int x=r.x;
        if(r.w!=d[x])continue;
        for(int k=first[x];k;k=a[k].next){
            int y=a[k].y;
            if(d[y]>d[x]+a[k].c)q.push((g){d[y]=d[x]+a[k].c,y});
        }
    }
    LL ans=0;
    for(i=0;i<mn;i++)if(d[i]<=b2){
        LL l=max(0ll,(b1-d[i])/mn);
        if(l*mn+d[i]<b1)l++;
        LL r=(b2-d[i])/mn;
        ans+=r-l+1;
    }
    printf("%lld\n",ans);
    return 0;
}

3.我真不想写背景

【题目描述】

某巨魔有一风扇,长得和下图一个怂样。这风扇原来有 N 个等分叶片,顺时针编号 1到 N(谁是编号 1 这不重要)。任意两个相邻的叶片间隔相等,大小质地相同。这个 N 满足 N=PaQb ,P,Q 为质数,a,b 为自然数。风扇一开始还不错,但是到后来风扇开始报复社会了。它的叶片从窗户甩了出去甩到了大街上,砸肿了二胖(二胖以前身材很好,你懂得)。风扇的叶片脱落是很危险的,因为这会使风扇失去平衡。可能当你经过风扇下方时它就直接拿你一血。但是左图风扇是平衡的,因为他的重心在风扇的正中心(至于为什么自己想吧)。现在给你一烂风扇。需要你判断它平不平衡,如果不平衡,那么判断它最少要拆掉它的多少叶片才能平衡。

【输入格式】

第一行两个数字 N,M,表示该风扇原来有 N 个叶片现在已经脱落了 M 个叶片。
接下来 M 行,每行一个数字,表示脱落的叶片编号,按升序给出。

【输出格式】

一个数字,表示为使电风扇平衡所需要拆掉的叶片的最少数量。

【样例输入输出】

damn.in
12 5
1
4
5
9
10
damn.out
0

【样例解释】

拆完后就是上图那怂电风扇。

【数据范围】

30%的数据:1N10
50%的数据:1N10000
100%的数据:1N2106MN

【题解】

然而这个题目其实要用网络流,本人并不会,还是放弃。

【代码】

#include <cstdio>

int main() {
    freopen("damn.in","r",stdin);
    freopen("damn.out","w",stdout);
    puts("0");//10分
    return 0;
}

总结

这次考试其实第二题没有往图论方面去想,导致了完全揹包走人的结果,不过似乎大家都没想出来。。。

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