1. 程式人生 > >18寒假13測

18寒假13測

poi 這一 cell sample 劃分 輸出 center using divide

題目名稱

buy

slide

divide

輸入

buy.in

slide.in

divide.in

輸出

buy.out

slide.out

divide.out

每個測試點時限

1秒

1秒

1秒

內存限制

256MB

256MB

256MB

測試點數目

10

10

10

每個測試點分值

10

10

10

是否有部分分

題目類型

傳統

傳統

傳統

buy

description:

地主zxr想買一些長方形的土地,所有的土地可以分為若幹組,每一組的土地的價格為這一組裏的最長的長乘上最長的寬。土地的長和寬是不能交換的,例如一塊2*5的土地和一塊5*2的土地放在一起,價格為5*5=25。ZXR想知道最少花費多少錢可以買下所有的土地。

Input:

第一行一個數n表示一共有n塊土地。

接下來n行每行兩個數xi和yi分別表示每塊土地的長和寬。

Output:

一行一個數表示最小價格。

Sampleinput:

4

100 1

15 15

20 5

1 100

Sampleoutput:

500

zxr分3組買這些土地:

第一組:100x1,

第二組1x100,

第三組20x5 和 15x15 plot.

每組的價格分別為100,100,300, 總共500.

HINT:

對於30%的數據:n<=2000。

對於100%的數據:n<=50000,長寬<=1e6

題解:先按x升序排序,再在此基礎上按y降序排序,如果不滿足此規則的點,則可以被覆蓋,如:

X: 20 40 60

Y;50 25 46 顯然二號點是沒有用的,他可被三號節點覆蓋;

於是我們得到了一個所有點都有用的序列,並且x,y的大小有序;

dp[i]表示選了1到 i 個點, dp[i] = dp[j] + Xi Yj+1 ,由這個形式我們可以想到斜率優化:如果有k>=j 並且k更優,則之後一定不會再用到j, k一定更優,通過轉移方程得到:

dp[j] + Xi Yj+1 >= dp[k] +Xi Yk+1 , k>j

所以滿足Xi >= (dp[k]-dp[j]) /(Yj+1 - Yk+1) 則k可跟新j,因為Xi遞增,於是斜率越大越好, 所以我們就維護一個上凸包,使斜率滿足上式且遞增,則隊首元素最優

#include <bits/stdc++.h>
using namespace std;

const int maxn = 50000+5;
const long long inf = 100000000000008;
long long dp[maxn];
int qq[maxn];
int n;
inline void read(long long &x){
    x=0;long long 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-0;s=getchar();} 
    x*=f;
}
struct Point{
    long long  x, y;
}p[maxn],q[maxn];
bool cmp(Point a, Point b){
    return a.x == b.x ? a.y > b.y : a.x < b.x; 
} 

int main(){
    freopen("buy.in","r",stdin);
    freopen("buy.out","w",stdout);
    scanf("%d",&n);
    int hd = 1;
    for(int i = 1; i <= n; i++)
        read(p[i].x), read(p[i].y);
    sort(p + 1, p + 1 + n, cmp);
    q[1] = p[1];
    for(int i = 1; i <= n; i++){
        while(hd && p[i].y >= q[hd].y)
            hd--;
        q[++hd] = p[i];
    }
        
    int h = 1, t = 1, s = 1;
    qq[1] = 0;
    for(int i = 1; i <= hd; i++){
        while(h < t && q[i].x * (q[qq[h]+1].y - q[qq[h+1]+1].y) > dp[qq[h+1]] - dp[qq[h]])
            h++;
        
        dp[i] = q[qq[h]+1].y * q[i].x + dp[qq[h]];
        while(h < t && (q[qq[t-1]+1].y - q[qq[t]+1].y) * (dp[i] - dp[qq[t]]) >= (dp[qq[t]] - dp[qq[t-1]]) * (q[qq[t]+1].y- q[i+1].y))
            t--;
        qq[++t] = i;
    }
    printf("%I64d\n",dp[hd]);
}

斜率優化要註意:加減符號和中間的變號過程; 斜率遞增建下凸包,遞減建上凸包

slide

description:

tony在一片雪地裏滑雪,他從(1,1)出發,並只能從高的地方滑到低的地方,貪玩的tony想知道他到底能滑多遠呢?

Input:

第一行兩個數n,m表示雪地大小。

接下來n行每行m個數表示雪地,其中w[i][j] = x表示在i,j位置的高度為x,每個位置(除了邊界)都可以滑到它的上下左右四個方向(從高往低)。

Output:

一行一個數表示滑行的最長長度。

Sample input:

22

4 3

21

Sample output:

3

Hint:對於30%的數據,n<=10,m<=10;

對於100%的數據,n<=300,m<=300。W[i][j]<=1e6;

題解:記憶化搜索的水題

#include <bits/stdc++.h>
using namespace std;

const int maxn = 305;
int n,m;
int mp[maxn][maxn],dp[maxn][maxn];
int a[4][2] = {{0,1},{1,0},{-1,0},{0,-1}};
int dfs(int x, int y){
    if(dp[x][y]) return dp[x][y];
    dp[x][y] = 1;
    int tmp = 0;
    for(int i = 0; i < 4; i++){
        int nx = x + a[i][0], ny = y + a[i][1];
        if(mp[nx][ny] < mp[x][y] && nx && ny && nx <=n &&ny <= m)
            tmp = max(tmp, dfs(nx, ny));
    }    
    return dp[x][y] += tmp;
}
int main(){
    freopen("slide.in","r",stdin);
    freopen("slide.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            scanf("%d",&mp[i][j]);
    dfs(1, 1);
    printf("%d\n",dp[1][1]);
}

divide

description:

tom想知道,把一個整數n劃分為若幹個正整數的形式,一共有多少種方案。例如當n=4時,他有5個劃分,{4},{3,1},{2,2},{2,1,1},{1,1,1,1};

input:

一行一個數n。

Output:

一行一個數表示方案數%1e9+7。

Sample input:

4

Sample output:

5

Hint:

對於30%的數據,n<=50。

對於100%的數據,n<=5000。

題解:dp[i][j]表示i拆分成的數中最大的數是j;

   i > j, dp[i][j] = dp[i-j][j] + dp[i][j-1] //有兩種情況,一是我選擇拆分的數中有j, 二是選擇的數中不含j

   i < j, dp[i][j] = dp[i][i] //之後會用到,意義也解釋的通

   i == j, dp[i][j] = 1 + dp[i][j-1]

#include <bits/stdc++.h>
using namespace std;

const int mod = 1e9+7;
const int maxn = 5005;
int ans,t, dp[maxn][maxn];

int main(){
    freopen("divide.in","r",stdin);
    freopen("divide.out","w",stdout);
    int n;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)dp[i][1] = 1;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++){
            if(i == j)dp[i][j] = 1 + dp[i][i-1];
            if(i > j)dp[i][j] = dp[i-j][j] + dp[i][j-1];
            if(i < j)dp[i][j] = dp[i][i];
            dp[i][j] %= mod;
        }
    printf("%d\n",dp[n][n]);
    
}

18寒假13測