1. 程式人生 > >2018.12.13考試總結

2018.12.13考試總結

自己社內出的一次小考。。。

感覺身體被掏空。。。知道是dp,但dp程式碼完全打不出來。。。。。。

深深地感到了恐懼。。。。。。

然後我就打的暴搜

2道題,暴搜出58分,竟然還能在53人中得14名。。。。。。

— — — — — — — — — — — — — — — 分割線 — — — — — — — — — — — — — —

總結一下    其實我覺得,暴搜才是王道,dp都是辣雞

1、儘量打正解,不會才打暴搜,雖然還是可以騙到些分,而且還不錯。

2、提高程式碼的首正率,不要花10分鐘打程式碼,30分鐘來除錯

3、思維要儘量拓寬,不要侷限於想到的第一種方法,多想幾種,也許第一種是錯的

— — — — — — — — — — — — — — — — 分割線 — — — — — — — — — — — — — — — —

下面是兩道題  其實都是改編的

題目大意:

1、monkey

有n棵樹,從左到右依次有它們的高度,現在有一群猴子從第一棵樹開始,都要前往最後一棵樹

已知第i只猴子的最長跳躍距離是k[i],也就是說,這隻猴子最多能從s跳到s+k[i]

如果這隻猴子落點的樹的高度大於等於TA原來的那棵樹的高度,TA的疲勞值就會加1

球每一隻猴子跳到最後一棵樹至少要累積多少疲勞值

 

2、telephonewire

有一排電線杆,各自有自己的高度height[i],現在我們需要把這一排電線杆順次連線起來

每連線一條電線所花的費用是TA與旁邊的電線杆的高度差*c

當然,我們也可以花x*x的代價時某根電線杆升高x米

求所需的最小費用

 

其實這裡還有更完整的::

第一題:monkey

時限:1s  記憶體256MB

【題目描述】有Q只猴子要從第一棵樹到第n棵樹去,第i只猴子一次跳躍的最遠距離為Ki。如果它在第x棵樹,那它最遠可以跳到第x+Ki棵樹。如果第j棵樹的高度比第i棵樹高或相等,那麼它從第i棵樹直接跳到第j棵樹,它的勞累值會增加1。所有猴子一開始在第一棵樹,請問每隻猴子要跳到第n棵樹花費的勞累值最小。

輸入格式:

第一行一個整數n,表示有n棵樹。(2<=n<=1000000)

接下來第二行給出n個正整數D1,D2,……,Dn(1<=Di<=10^9),其中Di表示第i棵樹的高度。

第三行給出了一個整數Q(1<=Q<=25),接下來Q行,給出了每隻猴子一次跳躍的最遠距離Ki(1<=Ki<=N-1)。

輸出格式:

輸出Q行,每行一個整數,表示一隻猴子的最小的勞累值。

輸入樣例:

9
4 6 3 6 3 7 2 6 5
2
2
5

輸出樣例:

2

1    

 

 

第二題:架設電話線(telephonewire)

時限1s,記憶體128M

【問題描述】Farmer John的奶牛們越來越不滿於牛棚裡一塌糊塗的電話服務 於是,她們要求FJ把那些老舊的電話線換成效能更好的新電話線。 新的電話線架設在已有的N(2 <= N <= 100,000)根電話線杆上, 第i根電話線杆的高度為heighti米(1 <= heighti <= 100)。 電話線總是從一根電話線杆的頂端被引到相鄰的那根的頂端 如果這兩根電話線杆的高度不同,那麼FJ就必須為此支付 C*電話線杆高度差(1 <= C <= 100)的費用。當然,你不能行動電話線杆, 只能按原有的順序在相鄰杆間架設電話線。Farmer John認為 加高某些電話線杆能減少架設電話線的總花費,儘管這項工作也需要支出一定的費用。 更準確地,如果他把一根電話線杆加高X米的話,他得為此付出X^2的費用。 請你幫Farmer John計算一下,如果合理地進行這兩種工作,他最少要在這個電話線改造工程上花多少錢。

【輸入格式】

* 第1行: 2個用空格隔開的整數:N和C

* 第2..N+1行: 第i+1行僅有一個整數:height_i

【輸出格式】

* 第1行: 輸出Farmer John完成電話線改造工程所需要的最小花費

【輸入樣例】

5 2
2
3
5
1
4

輸出樣例:

15

樣例說明:
一共有5根電話線杆,在杆間拉電話線的費用是每米高度差$2。
在改造之前,電話線杆的高度依次為2,3,5,1,4米。

最好的改造方法是:Farmer John把第一根電話線杆加高1米,把第四根加高2米,
使得它們的高度依次為3,3,5,3,4米。這樣花在加高電線杆上的錢是$5。
此時,拉電話線的費用為$2*(0+2+2+1) = $10,總花費為$15。

— — — — — — — — — — — — — — — — 分割線 — — — — — — — — — — — — — — — —

以下是題解::

第一題:

f[i]表示這隻猴子跳到第i棵樹所花的最小疲勞值,那麼

                                       f [ i ] = min ( f [ i ] , f [ j ] + ( h [ i ] > h [ j ] ) )

其中  i 1 ~ n,j i - m ~ i - 1

然後我們發現其實這個樹的高度其實是可以用一個單調佇列來記錄一下的

然後就可以把時間複雜度縮為O(nq)

以下是程式碼:

#include<cstdio>
#include<cstring>
#include<deque>
using namespace std;
inline void read(int &x) {
    x=0;
    int f=1;
    char s=getchar();
    while(s<'0'||s>'9') {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0'&&s<='9') {
        x=x*10+s-48;
        s=getchar();
    }
    x*=f;
}
inline void pr(int x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    if(x>9)
        pr(x/10);
    putchar(x%10+48);
}//快讀快輸不解釋
int a[1000005],dp[1000005];
int i,n,j,k,z,m;
deque<int>q;
int main() {
    read(n);
    for(i=1;i<=n;i++)
        read(a[i]);
    read(z);
    for(i=1;i<=z;i++) {
        read(m);
        q.push_back(1);//第一棵樹
        for(j=2;j<=n;j++) {//從第二棵樹到第n棵樹
            while(!q.empty()&&q.front()<j-m)//不能從這棵樹跳到此樹
                q.pop_front();//沒有用了,pop掉
            dp[j]=dp[q.front()]+(a[j]>=a[q.front()]);//計算dp的值,括號裡判斷疲勞值加不加一
            while(!q.empty())
                if(dp[q.back()]>dp[j]||(dp[q.back()]==dp[j]&&a[j]>a[q.back()]))
                //疲勞值更小或者疲勞值相同並且高度更高
                    q.pop_back();//pop出去
                else//不然就可以直接停止了
                    break;
            q.push_back(j);
        }
        pr(dp[n]),putchar('\n');
        q.clear();
        memset(dp,0,sizeof(dp));//別忘了清空
    }
}

第二題:

dp[i][j]表示第i根電線杆的高度是j米時前面所有的費用的最小值

最基礎的狀態轉移方程:

                                       f [ i ] [ j ] = ( j - h[ i ] ) * ( j - h[ i ] ) + min ( f [ i-1 ] [ k ] + c * | j - k | )

我們把這個方程裡的絕對值去掉,就可以得到

1、j>=k

                                       f [ i ] [ j ] = ( j - h [ i ] ) * ( j - h [ i ] ) + min ( f [ i-1 ] [ k ] - c * k +  c * j )

2、j<=k

                                       f [ i ]  [ j ] = ( j  - h[ i ] ) * ( j - h [ i ] ) + min ( f [ i - 1 ] [ k ] + c *  k - c * j )

這裡的等號放在哪裡都可以

在整理一下:

1、                                 f [ i ] [ j ] = ( j - h [ i ] ) ^ 2 + c * j + min ( f [ i - 1 ] [ k ] - c * k )

2、                                 f [ i ] [ j ] = ( j - h [ i ] ) ^ 2 - c * j + min ( f [ i - 1 ] [ k ] + c * k )

我們發現後面的這個min其實可以記錄一下,j 迴圈時再去更新min

這樣就可以把最裡面的一層O ( n ) 的縮為O ( 1 ),所以總時間複雜度是O ( n * maxh )

再上程式碼:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
inline void read(int &x) {
    x=0;
    int f=1;
    char s=getchar();
    while(s<'0'||s>'9') {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0'&&s<='9') {
        x=x*10+s-48;
        s=getchar();
    }
    x*=f;
}
inline void pr(int x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    if(x>9)
        pr(x/10);
    putchar(x%10+48);
}//快讀快輸不解釋
int a[100005],dp[100005][105],i,n,j,k,c,m,minn;
int main() {
	read(n),read(c);
	memset(dp,0x3f3f3f3f,sizeof(dp));//把所有的dp清為最大值,方便計算
	for(i=1;i<=n;i++)
		read(a[i]),m=max(m,a[i]);//這裡m取所有高度的最大值是為了節省時間
                                 //因為如果我們把某一個電線杆升高到m以上,那就是白費力氣
	for(i=a[1];i<=m;i++)
		dp[1][i]=(i-a[1])*(i-a[1]);//初始化,更新第一根電線杆高度為i時的費用
	for(i=2;i<=n;i++) {
		minn=2147483647;//每次都要更新
		for(j=0;j<a[i];j++)
			minn=min(minn,dp[i-1][j]-c*j);//因為只能升高,這裡相當於預處理一次
		for(j=a[i];j<=m;j++) {//順著更新一次
			dp[i][j]=minn+c*j+(j-a[i])*(j-a[i]);
			minn=min(minn,dp[i-1][j]-c*j);
		}
		minn=2147483647;
		for(j=m;j>=a[i];j--) {//倒著更新一次
			minn=min(minn,dp[i-1][j]+c*j);
			dp[i][j]=min(dp[i][j],minn-c*j+(j-a[i])*(j-a[i]));//狀態轉移方程
		}
	}
	minn=2147483647;
	for(i=a[n];i<=m;i++)
		minn=min(dp[n][i],minn);//根據dp的意思,在最後一根電線杆的高度上找min值
	pr(minn);
}