題目跳轉:
題目描述
小明的班上共有 n 元班費,同學們准備使用班費集體購買 3 種物品:
- 圓規,每個 7 元。
- 筆,每支 4 元。
- 筆記本,每本 3 元。
小明負責訂購文具,設圓規,筆,筆記本的訂購數量分別為 a,b,c,他訂購的原則依次如下:
- n 元錢必須正好用光,即 7a+4b+3c=n。
- 在滿足以上條件情況下,成套的數量盡可能大,即 a,b,c中的最小值盡可能大。
- 在滿足以上條件情況下,物品的總數盡可能大,即 a+b+c 盡可能大。
請你幫助小明求出滿足條件的最優方案。可以證明若存在方案,則最優方案唯一。
輸入格式
輸入僅一行一個整數,代表班費數量 n。
輸出格式
如果問題無解,請輸出 -1。
否則輸出一行三個用空格隔開的整數 a, b, c,分別代表圓規、筆、筆記本的個數。
輸入輸出樣例
輸入 #1
1
輸出 #1
-1
輸入 #2
14
輸出 #2
1 1 1
輸入 #3
33
輸出 #3
1 2 6
說明/提示
樣例輸入輸出 3 解釋
a=2,b=4,c=1 也是滿足條件 1,2 的方案,但對於條件 3,該方案只買了 7 個物品,不如 a=1,b=2,c=6 的方案。
數據規模與約定
-
對於測試點 1 ~ 6,保證n≤14。
-
對於測試點 7 ~ 12,保證 n是 14 的倍數。
-
對於測試點 13 ~18,保證 n≤100。
-
對於 100% 的數據,保證 0≤n≤\(10^5\)。
題解部分
【題目大意】給定三件物品a,b,c的價格以及總金額n,求問訂購原則的情況下,最優的購買方案是什么?
【思路分析】如果在分析題目樣例的時候,能夠人工模擬拼湊出它的購買方案,那不難想到使用枚舉算法解決。但是你全部枚舉a,b,c的話肯定會超時的,所以我們還需要對枚舉算法進行一定的優化。那么怎么來優化呢?
從題目的三個條件來入手:
- n 元錢必須正好用光,即 7a+4b+3c=n。
- 在滿足以上條件情況下,成套的數量盡可能大,即 a,b,c中的最小值盡可能大。
- 在滿足以上條件情況下,物品的總數盡可能大,即 a+b+c 盡可能大。
首先,第一個條件,n元錢必須用完,那就要我們保證不能剩下錢,這里沒什么好優化的。
再來看第二個條件,成套的數量盡可能大,那就是說明盡可能地整體買入這三樣東西,他們的總額是14元。那么我們可以先處理一個maxt,maxt=n/14,代表n元錢最多能賣maxt套。接下來就可以從maxt枚舉到0,看有沒有合法方案。
最后,第三個條件,物品總數盡可能大,總數盡可能多,那么我就要盡可能買價格便宜的,那才能盡可能多。所以買完一整套后,剩余的money,money=n-(maxt*14),就先全部買入c,套數為maxc=money/3。接着再循環maxc~0,其中b的套數就可以通過計算得到,就不要枚舉了,其實這道題有點像百錢買百雞的變形。
通過以上幾個條件的約束,枚舉范圍就變得很小了,就可以通過此題,並且注意枚舉都是從大到小倒着枚舉的
Talk is cheap,上代碼
【參考代碼】
#include <bits/stdc++.h>
using namespace std;
int n,ta,tb,tc; //n為總的金錢,ta,tb,tc存買的套數
int money,moneyb,maxc; //money是整套買了之后的剩下的錢,c是枚舉的筆和筆記本套數,moneyb是買了a、c之后剩下的錢
int main()
{
int flag=0; //0代表當前沒有方案
cin>>n;
int maxt=n/14; //為了滿足第二個條件,先3件物品整套買
for(int i=maxt;i>=0;i--){ //從最大套數枚舉到最小
ta = tb = tc = i ; //套數初始為i
money=n-(i*14); //算出剩下的錢
maxc=money/3; //剩下的錢能買入的最多c的套數
for(int c=maxc;c>=0;c--){ //為了滿足第三個條件,那么就要讓3塊錢的物品盡可能的多
moneyb=(money-3*c); //算出能夠買下物品b剩余的錢
if(moneyb%4==0){ //如果剩下的錢能買完物品b
flag=1; //那么就能產生方案
if(3*i+moneyb/4+c>ta+tb+tc){ //更新ta,tb,tc的值
ta=i;
tb=i+moneyb/4;
tc=i+c;
}
}
if(flag){ //輸出方案
printf("%d %d %d\n",ta,tb,tc);
return 0;
}
}
}
printf("-1"); //否則沒有方案
return 0;
}
題目描述
小 H 是一個熱愛運動的孩子,某天他想給自己制定一個跑步計划。小 H 計划跑 \(n\)米,其中第$ i(i \geq 1)$ 分鍾要跑 \(x_i\)米($x_i $是正整數),但沒有確定好總時長。
由於隨着跑步時間增加,小 H 會越來越累,所以小 H 的計划必須滿足對於任意 \(i(i >1)\) 都滿足$ x_i \leq x_{i-1}$。
現在小 H 想知道一共有多少個不同的滿足條件的計划,請你幫助他。兩個計划不同當且僅當跑步的總時長不同,或者存在一個 \(i\),使得兩個計划中 \(x_i\) 不相同。
由於最后的答案可能很大,你只需要求出答案對 \(p\) 取模的結果。
輸入格式
輸入只有一行兩個整數,代表總米數 \(n\) 和模數 \(p\)。
輸出格式
輸出一行一個整數,代表答案對 \(p\) 取模的結果。
輸入輸出樣例
輸入 #1
4 44
輸出 #1復制
5
輸入 #2
66 666666
輸出 #2
323522
輸入 #3
66666 66666666
輸出 #3
45183149
說明/提示
樣例輸入輸出 1 解釋
五個不同的計划分別是:{1,1,1,1},{2,1,1},{3,1},{2,2},{4}。
數據規模與約定
本題共 10 個測試點,各測試點信息如下表。
測試點編號 | \(n \leq\) | 測試點編號 | \(n \leq\) |
---|---|---|---|
1 | 5 | 6 | \(2\times 10^3\) |
2 | 10 | 7 | \(5\times 10^3\) |
3 | 50 | 8 | \(2\times 10^4\) |
4 | 100 | 9 | \(5\times 10^4\) |
5 | 500 | 10 | \(10^5\) |
對於全部的測試點,保證$ 1 \leq n \leq 10^5$,\(1 \leq p < 2^{30}\)。
題解部分
【題目大意】給出一個數n,求多少種拆分方案?
【思路分析】裸的拆分數問題,這道題的滿分解法超出了大家的知識范圍,建議大家可以去做簡單版【FZOJ 1297 數的划分】一題。
滿分解法要求時間復雜度控制在\(O(n\sqrt(n))\)
滿分解法一:完全背包DP+分塊
滿分解法二:數學知識——五邊形數定理
【參考代碼】
下面代碼借鑒自洛谷
解法一:
//完全背包+分塊
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int f[N];
int g[400][N];
int main() {
int n, p;
cin >> n >> p;
int m = sqrt(n) + 1;
f[0] = 1;
for (int i = 1; i < m; i++) {
for (int j = i; j <= n; j++) {
f[j] += f[j - i];
f[j] %= p;
}
}
g[0][0] = 1;
for (int i = 1; i < m; i++) {
for (int j = i; j <= n; j++) {
g[i][j] = g[i][j - i];
if (j >= m) g[i][j] += g[i - 1][j - m];
g[i][j] %= p;
}
}
int ans = 0;
for (int i = 0; i <= n; i++) {
LL sum = 0;
for (int j = 0; j < m; j++) sum += g[j][n - i];
sum %= p;
ans = (ans + f[i] * sum) % p;
}
cout << ans << endl;
return 0;
}
解法二:
//五邊形數定理
#include<bits/stdc++.h>
int T,n,m,mod,f[100010],g[100010];
int main(){
f[0]=1,scanf("%d%d",&n,&mod);
for(int i=1;i*(3*i-1)/2<=n;i++)g[m++]=i*(3*i-1)/2,g[m++]=i*(3*i+1)/2;
for(int i=1;i<=n;i++)for(int j=0;j<m&&g[j]<=i;j++)f[i]=(f[i]+(((j>>1)&1)?-1ll:1ll)*f[i-g[j]])%mod;
printf("%d\n",(f[n]+mod)%mod);
}
題目描述
C 國由 \(n\) 座城市與 \(m\) 條有向道路組成,城市與道路都從 1 開始編號,經過 \(i\) 號道路需要 \(t_i\) 的費用。
現在你要從 1 號城市出發去 \(n\) 號城市,你可以施展最多 \(K\) 次魔法,使得通過下一條道路時,需要的費用變為原來的相反數,即費用從 \(t_i\) 變為 -\(t_i\)。請你算一算,你至少要花費多少費用才能完成這次旅程。注意:使用魔法只是改變一次的花費,而不改變一條道路自身的$ t_i$;最終的費用可以為負,並且一個城市可以經過多次(包括 \(n\) 號城市)。
輸入格式
從文件 magic.in 中讀入數據。
第一行三個整數$ n, m, K$,表示城市數、道路數、魔法次數限制。
接下來 m 行每行三個整數 \(u_i,v_i,t_i\),第 \(i\) 行描述 \(i\) 號道路,表示一條從 \(u_i\) 到 \(v_i\) 的有向道路,經過它需要花費 \(t_i\)。
輸出格式
輸出到文件 magic.out 中。
僅一行一個整數表示答案。
樣例1輸入
4 3 2
1 2 5
2 3 4
3 4 1
樣例1輸出
-8
樣例1解釋
依次經過 1 號道路、2 號道路、3 號道路,並在經過 1、2 號道路前使用魔法。
樣例2輸入
2 2 2
1 2 10
2 1 1
樣例2輸出
-19
樣例2解釋
依次經過 1 號道路、2 號道路、1 號道路,並在兩次經過 1 號道路前都使用魔法。
數據范圍與提示
對於所有測試點和樣例滿足:
1 ≤ n ≤ 100,1 ≤ m ≤ 2500,0 ≤ K ≤ 106,1 ≤ ui,vi ≤ n,1 ≤ ti ≤ 109。
數據保證圖中無自環,無重邊,至少存在一條從 1 號城市到達 n 號城市的路徑。
每個測試點的具體限制見下表:
題解部分
【前置知識】快速冪、矩陣乘法、最短路floyed
【題目大意】n個城市,m條路,構成一張圖。但是在這張圖上,邊權允許有k次改變,問最少的花費(最短路)。
【思路分析】一眼看完題,心中大呼:這不就是分層圖裸題嗎?
后來稍微分析了一下時間復雜度和數據規模,特別是看了k的最大值\(10^6\),顯然分層圖不可行,時間和空間都會爆炸。
這道題目的時間復雜度肯定不能與\(k\)相關,或者盡可能把\(k\)
變成\(\sqrt k\)或者\(\log k\),這樣才能保證通過。
所以就有了Floyed求最短路+矩陣優化快速冪的做法
【參考代碼】
下面代碼借鑒自洛谷
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
struct edge
{
int u,v,w;
}e[2505];
int n,m,k;
struct mat
{
long long a[105][105];
mat(int x=63)
{
memset(a,x,sizeof(a));
}
mat operator*(const mat&b)const
{
mat ans;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans.a[i][j]=min(ans.a[i][j],a[i][k]+b.a[k][j]);
return ans;
}
}a;
long long f[105][105];
mat fpow(mat x,int y)
{
mat ans;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans.a[i][j]=f[i][j];
while(y)
{
if(y&1)ans=ans*x;
x=x*x;
y>>=1;
}
return ans;
}
int main()
{
memset(f,63,sizeof(f));
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
f[i][i]=0;
for(int i=1;i<=m;i++)
{
cin>>e[i].u>>e[i].v>>e[i].w;
f[e[i].u][e[i].v]=e[i].w;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
for(int k=1;k<=m;k++)
{
int u=e[k].u,v=e[k].v,w=e[k].w;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a.a[i][j]=min(a.a[i][j],min(f[i][j],f[i][u]+f[v][j]-w));
}
if(k==0)cout<<f[1][n]<<endl;
else cout<<fpow(a,k).a[1][n]<<endl;
return 0;
}