1. 程式人生 > >2018/7/19 考試(tower,work,holes)

2018/7/19 考試(tower,work,holes)

while 輸入 -- 序列 計劃 我只 長大 做到 std

noip模擬賽,挺良心的題,考的賊爛(膜一下@來日方長大佬(sdfz rank1))

不多說了,看題吧

1.tower

題面:

鐵塔(tower.pas/c/cpp)

題目描述

Rainbow和Freda要在PoeticIsland市的一座山腳下蓋房子定居了......
蓋房子需要鋼材,幸運的是,這裏有排成一行的n座廢棄的鐵塔,從左到右編號為1~n,其中第i座的高度為h[i]。
Rainbow和Freda想蓋一座上面小下面大的城堡,並且城堡的層數盡可能多。
因此,他們要把這些鐵塔分成盡量多組,每組內的鐵塔編號必須是連續的,並且從左到右各組內鐵塔的高度之和單調不減。
最後,他們會用每組鐵塔所提供的鋼材構成一層城堡。

但是Rainbow和Freda簡直弱爆了有木有,於是請你幫忙計算一下最多能分成多少組呢?

輸入格式:

第一行一個整數n。第二行n個整數,第i個整數表示h[i]。

輸出格式:

輸出一個整數,表示(n-最多能分成的組數)。

思路:

正解是DP,我手打了一個貪心,20分。。。。。

貪心可以被證明是錯的,(2,2,1,3,3,自己看一下),爆搜也會超時

怎麽辦呢?

DP

我們要求的其實就是將原序列劃分為多個子串

且每一個子串長度和連續不降

所以我們可以用前綴和預處理一下

然後開始DP

我們要保證在最後面的合起來的數盡可能的小

所以維護3個數組

dp數組,前綴和數組和最大值數組

線性轉移即可

代碼(感謝顏神)

#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
inline int get()
{
    int n;
    char c;
    while((c=getchar())||1)
    {
        if(c>=0&&c<=9)
        {
            break;
        }
    }
    n
=c-0; while((c=getchar())||1) { if(c>=0&&c<=9) { n=n*10+c-0; } else { return(n); } } } int dp[5001][5001]; int ints[5001],sums[5001]; inline int he(int l,int r) { if(l==0) { return(sums[r]); } return(sums[r]-sums[l-1]); } signed main() { // freopen("tower.in","r",stdin); // freopen("tower.out","w",stdout); memset(dp,128,sizeof(dp)); int n=get(); for(register int i=1;i<=n;i++) { ints[i]=get(); sums[i]=sums[i-1]+ints[i]; } for(register int i=1;i<=n;i++) { dp[i][1]=1; } for(register int j=2;j<=n;j++) { int mx=-1234567890,k=j; for(register int i=j;i<=n;i++) { while(k>1&&he(k-1,j-1)<=he(j,i)) { k--; mx=max(mx,dp[j-1][k]); } if(k<j) { dp[i][j]=mx+1; } } } int maxn=0; for(register int i=1;i<=n;i++) { maxn=max(maxn,dp[n][i]); } cout<<n-maxn<<endl; fclose(stdin); fclose(stdout); return(0); }

T2:work

題面:

工作計劃(work.pas/c/cpp)

題目描述:

Mark 在無意中了解到了Elf的身世。
在和James商量過之後,好心的他們打算送Elf返回故鄉。
然而,去往Gliese的飛船票價高的驚人,他們暫時還付不起這筆費用。
經過一番考慮,Mark打算去額外做一些工作來獲得收入。
經過一番調查,Mark發現有N個工作可以做。
做第i件工作所需要的時間為Di,同時也需要一個能力值Ci才可以去做。
每件工作都可以在任意時間開始,也可以做任意多次。
所有的工作給付的報酬都是一致的。
同時,有S個課程可以參加,我們認為今天是第0天,第i個課程在第Mi天開始,持續時間為Li天,課程結束之後能力值會變為Ai。
現在Mark 的能力值為1。
Mark 只能做工作到第T天(因為那是飛船起飛的日子)。
他想知道期限內他最多可以做多少件工作,好決定未來的打算。
於是他找到了applepi。でも、applepiは彼女と一緒に楽しむことが大切だ,(本人翻譯:但是applepi和他的女朋友在一起享受是很重要的)所以這個任務就交給你了。

輸入格式:

第一行包含三個空格分隔的整數T,S,N。
之後S 行,每行三個整數M,L,A,描述一個課程。
之後N 行,每行兩個整數C,D,描述一件工作。

輸出格式:

一個整數,表示Mark 最多可以做多少件工作。

思路:

標配DP,切了1個半小時

我們首先去重,確保每個能力值對應的是當前能力值能做到最大的工作效率(比如說能力都是3,一個要3天另一個要4天,我只存3天)

然後我們開始dp

dp數組我開兩維,一維代表時間,另一維代表能力

dp[i][j]表示在i這個位置,能力值為j時的最大收益

如果這個位置我們做工作,那麽我們工作結束時的最大收益就可以由當前+1轉移而來

如果這個位置我們學習,那麽我們到學習結束時的最大收益就是當前的最大收益

當然,我們一開始要將數組賦值為負無窮

這樣不可能的情況可以被自動排除

代碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define rii register int i
#define rij register int j
#define inf 1<<30
using namespace std;
int gz[105];
struct kc{
    int fi[105],nl[105],sl;
}y[10005];
int n,t,s,dp[200005][105],mnl;
int main()
{
//    freopen("wrk.in","r",stdin);
//    freopen("wrk.out","w",stdout);
    scanf("%d%d%d",&t,&s,&n);
    for(rii=1;i<=s;i++)
    {
        int ltt,kkk,lzn;
        scanf("%d%d%d",&ltt,&kkk,&lzn);
        y[ltt].sl++;
        y[ltt].fi[y[ltt].sl]=kkk+ltt;
        y[ltt].nl[y[ltt].sl]=lzn;
        mnl=max(mnl,lzn);
    }
    for(rii=1;i<=105;i++)
    {
        gz[i]=inf;
    }
    for(rii=1;i<=n;i++)
    {
        int ltt,kkk;
        scanf("%d%d",&ltt,&kkk);
        gz[ltt]=min(gz[ltt],kkk);
    }
    int cnt=0;
    for(rii=0;i<=10000;i++)
    {
        for(rij=0;j<=100;j++)
        {
            dp[i][j]=-inf;
        }
    }
    dp[0][1]=0;
    for(rii=0;i<=t-1;i++)
    {
        int minx=inf;
        int maxn=0;
        for(rij=1;j<=mnl;j++)
        {
            if(i!=0)
            {
                dp[i][j]=max(dp[i][j],dp[i-1][j]);
            }
            minx=min(minx,gz[j]);
            if(minx==inf)
            {
                continue;
            }
            dp[i+minx][j]=max(dp[i+minx][j],dp[i][j]+1);
            maxn=max(maxn,dp[i][j]);
        }
        for(rij=1;j<=y[i].sl;j++)
        {
            dp[y[i].fi[j]][y[i].nl[j]]=max(dp[y[i].fi[j]][y[i].nl[j]],maxn);
        }
    }
    int ans=0;
    for(rii=1;i<=100;i++)
    {
        ans=max(dp[t][i],ans);
    }
    cout<<ans;
    return 0;
}
    

3.holes

題面

樹洞(holes.pas/c/cpp)

題目描述

在一片棲息地上有N棵樹,每棵樹下住著一只兔子,有M條路徑連接這些樹。更特殊地是,只有一棵樹有3條或更多的路徑與它相連,其它的樹只有1條或2條路徑與其相連。
換句話講,這些樹和樹之間的路徑構成一張N個點、M條邊的無向連通圖,而度數大於2的點至多有1個。
近年以來,棲息地頻繁收到人類的侵擾。
兔子們聯合起來召開了一場會議,決定在其中K棵樹上建造樹洞。
當危險來臨時,每只兔子均會同時前往距離它最近的樹洞躲避,路程中花費的時間在數值上等於距離。
為了在最短的時間內讓所有兔子脫離危險,請你安排一種建造樹洞的方式,使最後一只到達樹洞的兔子所花費的時間盡量少。

輸入格式

第一行有3個整數N,M,K,分別表示樹(兔子)的個數、路徑數、計劃建造的樹洞數。
接下來M行每行三個整數x,y,表示第x棵樹和第y棵樹之間有一條路徑相連。1<=x,y<=N,x≠y,任意兩棵樹之間至多只有1條路徑。

輸出格式

一個整數,表示在最優方案下,最後一只到達樹洞的兔子所花費的時間。

思路:

一眼看出是一道樹上二分

發現只剩下30分鐘了,來不及寫

於是50pts暴力走人

怎麽二分呢?

我們知道他有k個樹洞

我們就可以枚舉長度

判斷此時是否成立即可

2018/7/19 考試(tower,work,holes)