18寒假13測
題目名稱 |
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測