動態規劃測試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;
}

總結

這次考試其實第二題沒有往圖論方面去想,導致了完全揹包走人的結果,不過似乎大家都沒想出來。。。

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