文章目錄
A C2-妙妙趣和值
題面
定義一個長度爲的數組的妙妙趣值是
那麼請問對於一個長度爲的序列
,所有長度爲的子序列
的妙妙趣值和爲多少。
某個序列的子序列
是從最初序列通過去除某些元素但不破壞餘下元素的相對位置(在前或在後)而形成的新序列。
答案爲100000007
取模
輸入
第一行兩個數,表示序列的長度,子序列的長度。
接下來一行,個整數
輸出
一行一個數,表示妙妙趣和值
輸入樣例
4 3
1 7 3 5
輸出樣例
8
樣例解釋
長度爲3的子序列有 [1,7,3], [1,3,5], [7,3,5], [1,7,5],每一個妙妙趣值都是2
思路
子序列
:某個序列的子序列
是從最初序列通過去除某些元素但不破壞餘下元素的相對位置(在前或在後)而形成的新序列。即不要求連續,只要求相對位置保持不變。
難題
首先考慮一點,妙妙趣值與子序列的順序沒有關係。
進一步考慮,我們更改原序列的順序後,對妙妙趣值和沒有影響。
所以我們先對原序列進行排序。
然後考慮一個問題,在一個非下降序列中,長度爲(注意不是妙妙趣值是)的子序列有多少個。
這個問題是dp可做的。
設是區間裏的,長度爲,的序列個數。
那麼的,其中是滿足的最大值。
如何統計答案?
注意是的子序列個數。
那麼的子序列個數就是
這裏指的是枚舉,的值
答案就要加上
更好的方式是,我們只需要枚舉x,把dp[n][k]加到答案裏即可。
爲什麼這樣正確?
注意dp是子序列個數。
一個子序列如果妙妙趣值爲y,那麼在枚舉x爲[1,𝑦]的時候這個子序列都會被統計一次。相當於貢獻了y。
這個轉化就相對於計算,只需要從後往前算一下前(後)綴和,然後再
那我們枚舉x的上界是多少呢?
每個值最大是,如果枚舉到,複雜度高達
事實上我們只需要枚舉到即可,思考一下爲什麼
複雜度爲,可以認爲被越掉,計爲
代碼
#include <iostream>
#include <algorithm>
using namespace std;
typedef long double ld;
typedef long long ll;
const int mod = 100000007;
const int ms = 1005;
int n, k;
int a[ms], dp[ms][ms];
int solve(int x){
dp[0][0] = 1;
int li = 0;
for (int i = 1; i <= n; ++i)
{
while (a[i] - a[li + 1] >= x) li++;
dp[i][0] = dp[i - 1][0];
for (int j = 1; j <= k && j <= i; ++j)
{
dp[i][j] = dp[i - 1][j] + dp[li][j - 1];
dp[i][j] %= mod;
}
}
return dp[n][k];
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> k;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
sort(a + 1, a + n + 1);
int ans = 0;
for (int i = 1; i * (k - 1) <= a[n]; i++)
{
ans = ans + solve(i);
ans %= mod;
}
cout << ans;
return 0;
}
B C2-與非門
題面
C C2-Zexal的鋼管切割
題面描述
在鋼管切割的背景下,已經知道長度爲1−n的鋼管的價值,給定長度爲n的鋼管在切割若干次(也可以不切割)所帶來的最小价值是?
輸入
多組數據輸入
第一行一個整數n,爲起始鋼管長度(0<n≤1000)
第二行n個整數,分別爲長度爲i的鋼管的價值ti(0<ti≤106)
輸出
對於每組數據,輸出一行,爲這根鋼管所帶來的最小价值T
輸入樣例
3
2 3 7
輸出樣例
5
思路
算法導論第三版15.1節。書本例題求最大值,求最小值將max換爲min即可
代碼
腦殘的化成了負值,爲了用max思路。。
#include <iostream>
#include <cstdio>
int n;
int t[1010],f[1000001];
int max(int a,int b){
return a>b?a:b;
}
int main() {
while(~scanf("%d",&n)){
f[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&t[i]);
t[i]=-t[i];
}
for(int i=1;i<=n;i++){
int q=-1000010;
for(int j=1;j<=i;j++){
q=max(q,t[j]+f[i-j]);
}
f[i]=q;
}
printf("%d\n",-f[n]);
}
return 0;
}
D C2-Zexel的流水線問題
題目描述
Zexal的偶像SkyLee趁着假期來逛漫展,漫展只有一條通道,只能從入口進入,出口離開,且左邊有n個店鋪,右邊也有n個店鋪,編號都是1~n。爲了照顧大家沒法兼顧逛兩邊的痛苦,左邊和右邊編號相同的店鋪賣同樣的周邊。
現在SkyLee站在入口處的路中間,立志要從頭到尾按順序逛完所有的店鋪。
因爲每家店鋪排隊的人數不同,SkyLee在左邊第i家要停留 p1[i] 的時間,在右邊第i家要停留 p2[i] 的時間。而且這條路還挺寬的,所以從左邊第i家移動到右邊第i+1家需要t[1][i]的時間;從右邊第i家移動到左邊第i+1家需要t[2][i]的時間。
由於時間安排的緊,SkyLee想知道逛完漫展至少需要多長時間。
開始時從路中間到任意一邊的時間以及同一邊相鄰店鋪移動時間忽略不計。
輸入
多組數據輸入
對於每一組測試數據,第一行1個數 n (0<=n<=500)表示店鋪數 。
接下來一行 n 個數,表示在左邊店鋪停留時間 p1 。再接下來一行n個數,表示在右邊店鋪停留時間 p2 (0<=p1,p2<=1000)。
接下來2行,每行n−1個數,分別表示 t[1][i] 和 t[2][i] (0<=t<=300)。
輸出
對於每組數據,輸出一行,爲最少時間T。
輸入樣例
3
10 1 10
8 5 10
3 1
1 3
輸出樣例
20
思路
課件例題,車間流水線。注意開始和結束時的時間安排,這題不耗時
代碼
#include<stdio.h>
#define MAXN 501
int p1[MAXN],p2[MAXN];//當前店鋪停留時間
int t1[MAXN],t2[MAXN];//t1[i]:從左邊第i家到右邊第i+1家所需的移動時間
int f[2][MAXN];//記錄數組
int Min(int a,int b);
int main()
{
int n;//店鋪數
while(scanf("%d",&n) == 1){
int i;
for(i = 1;i <= n;i++)
scanf("%d",&p1[i]);
for(i = 1;i <= n;i++)
scanf("%d",&p2[i]);
for(i = 1;i <= n-1;i++)
scanf("%d",&t1[i]);
for(i = 1;i <= n-1;i++)
scanf("%d",&t2[i]);
//初始化
f[0][1] = p1[1];
f[1][1] = p2[1];
//狀態轉移
int j;
for(j = 2;j <= n;j++){
f[0][j] = Min(f[0][j-1]+p1[j],f[1][j-1]+t2[j-1]+p1[j]);
f[1][j] = Min(f[1][j-1]+p2[j],f[0][j-1]+t1[j-1]+p2[j]);
}
if(f[0][n] >= f[1][n])
printf("%d\n",f[1][n]);
else
printf("%d\n",f[0][n]);
}
}
int Min(int a,int b)
{
if(a < b)
return a;
else
return b;
}
E C2-Zexal的排座位
題目描述
在一個班級中挑選N個學生排成一列座位(保證有足夠多的男生與足夠多的女生),要求座位序列中男生互不相鄰,求解有多少種排列方式?(挑選男生與女生的數量與排列方式均爲任意)
例如挑選三個學生,那麼所有排列爲: 女女女、女男女,男女女,女女男,男女男
輸入
第一個數爲學生總數N(0<N<30)
輸出
只有一行,保證男生與男生不相鄰,座位排列的所有情況數目的結果
輸入樣例
3
輸出樣例
5
思路
已排好n個座位,第n+1個座位的性別根據第n個座位的性別做出選擇,故n+1的座位的排序方法由n個座位的排序方法得來。
代碼
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int ms = 50;
ll res[ms][2];
int main(){
res[1][0] = res[1][1] = 1;
for(int i = 2; i <= 30; ++i){
res[i][0] = res[i - 1][0] + res[i - 1][1];
res[i][1] = res[i - 1][0];
}
int n;
while (cin >> n){
cout << res[n][0] + res[n][1] << "\n";
}
return 0;
}
F C2-Zexal的競賽
題目描述
在一場競賽中有n道題目,每道題都會對應一個分值,你可以選擇任意一道題作答,比如你選擇了x分的題目,那麼在作答完畢(假設一定可以得分)後,你將獲得x分,但是這場比賽中分值等於x−1和x+1的其他題目就會消失,那麼這場比賽中你最多可以得到多少分?
輸入
第一個數爲題目總數
接下來爲 個整數
輸出
輸出你可以得到的最高分
輸入樣例
9
1 2 1 3 2 2 2 2 3
輸出樣例
10
樣例解釋
每次都選擇2 選擇5次即可得到10分 1和3根據題目要求會消失
思路
當選擇第n時,考慮第n-1個是否選了。
這樣看是一個經典的dp問題。
if(i == 0) dp[i] = 0;
if(i==1) dp[i]=num[i];
if(i>1) dp[i] = max(dp[n-2]+num[i],dp[n-1])
代碼
#include<iostream>
#include<cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int ms = 1e5 + 10;
int cnt[ms], dp[ms][2];
int main()
{
int n, a;
cin >> n;
for (int i = 0; i < n; ++i)
{
cin >> a;
cnt[a]++;
}
for (int i = 1; i < ms; ++i)
{
dp[i][0] = max(dp[i - 1][1], dp[i - 1][0]);
dp[i][1] = dp[i - 1][0] + cnt[i] * i;
}
cout << max(dp[ms - 1][0], dp[ms - 1][1]);
return 0;
}