一共三道題, 三道我都不會, 入門組??????
所謂的“入門組”題目
1. 文具訂購 題目鏈接:普及難度
題目描述
小明的班上共有n元班費, 同學們準備集體用這些班費購買3種物品:
1.圓規, 每個7元
2.筆,每隻4元
3.筆記本,每本3元
小明負責訂購文具, 設圓規, 筆, 筆記本的訂購數量分別爲a, b, c, 他訂購的原則依次
如下:
1.n元錢必須正好用光, 即
2.在滿足以上條件的情況下, 成套的數量級儘可能地大, 即
3.在滿足以上要求的前提下, 物品的總數儘可能地大, 即
請你幫助小明求出滿足條件的最優方案。可以證明最優方案存在且唯一。
題目分析
我們來一條一條分析(記住有優先級)
第一個條件:就是說, 我們要用, 7, 4, 3這3個數正好湊出n來;
可見, 除了1, 2, 5之外, 其他的都可以湊出來。
第二個條件:最小值儘量大, 那麼其他兩個都要比他大, 可見, 貪心策略就是湊出儘量多的成套的(一套14元)的文具來。
第三個條件, 因爲總數要儘量多, 所以當成套的湊完之後, 還剩下的錢, 就要往3元和4元去湊, 因爲第三個條件要求文具數量儘可能多, 所以如果能湊出一個7元的, 7 = 3+4, 所以, 可以轉換爲一個3元的加上一個4元的, 這樣文具數量就比之前多1了.
現在有一個問題, 萬一湊完整套之後餘下的錢是1, 2, 5怎麼辦??
把一套的錢拆開, 加入到現在剩下的錢裏, 也就是餘下的錢加14, 不就能拆開了嗎。
代碼
#include <cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<cstdlib>
#include<ctime>
#include<cctype>
using namespace std;
int n;
int main()
{
cin >> n;
if(n == 1 || n == 2 || n == 5)
{
cout << -1 << endl;
return 0;
}
int a, b, c;
int j = n % 14;
int k = n / 14;
if(j == 0)
{
a = b = c = k;
cout << a << " " << b << " " << c << endl;
}
else if(j == 1)
{
a = b = k-1;
c = k+4;
cout << a << " " << b << " " << c << endl;
}
else if(j == 2)
{
a = k-1;
b = k;
c = k + 3;
cout << a << " " << b << " " << c << endl;
}
else if(j == 3)
{
a = b = k;
c = k+1;
cout << a << " " << b << " " << c << endl;
}
else if(j == 4)
{
a = c = k;
b = k+1;
cout << a << " " << b << " " << c << endl;
}
else if(j == 5)
{
a = k-1;
b = k;
c = k+4;
cout << a << " " << b << " " << c << endl;
}
else if(j == 6)
{
a = b = k;
c = k+2;
cout << a << " " << b << " " << c << endl;
}
else if(j == 7)
{
a = k;
b = c = k+1;
cout << a << " " << b << " " << c << endl;
}
else if(j == 8)
{
a = c = k;
b = k+2;
cout << a << " " << b << " " << c << endl;
}
else if(j == 9)
{
a = b = k;
c = k + 3;
cout << a << " " << b << " " << c << endl;
}
else if(j == 10)
{
a = k;
b = k+1;
c = k+2;
cout << a << " " << b << " " << c << endl;
}
else if(j == 11)
{
a = k;
b = k+2;
c = k+1;
cout << a << " " << b << " " << c << endl;
}
else if(j == 12)
{
a = b = k;
c = k+4;
cout << a << " " << b << " " << c << endl;
}
else
{
a = k;
b = k+1;
c = k+3;
cout << a << " " << b << " " << c << endl;
}
return 0;
}
2.跑步 題目網址 :省選難度
題目描述
小H是一個熱愛運動的孩子, 某天他想給自己指定一個跑步計劃。計劃一共跑N米, 其中 第分鐘要跑米(是正整數, 但沒有確定好總時長。
由於隨着跑步時間的增加, 小H會越來越累, 所以小H的計劃必須滿足對於任意都滿足.
現在小H想知道一共存在多少個不同的滿足條件的計劃, 請你幫助他,兩個計劃不同當且僅當跑步的總時長不同, 或者存在一個i, 是的兩個計劃中的不相同。
由於最後的答案可能很大, 你只需要輸出答案對p取模的結果。
題目分析
一道很經典的正整數拆分問題 ,首先搞明白什麼時候算作一種情況:當且僅當每個數字的組合沒有與之前重複的即可, 不考慮順序。也沒有數字個數上的要求。
考慮dp;設表示用了前i個數和爲j方案數。
則他就有第i個數選和不選兩種選擇:非常像完全揹包有木有???
因此我們可以是他變成一維的。
但是這樣容易超時, 所以我們決定實行分塊操作。
設則小於m的數組成得方案我們可以用f數組去處理。大於m的數組成的方案則設一個g數組。
表用了i個大於等於m的數和爲j的方案數。
兩種操作:
在拆分序列中增加一個數m;
把當前在拆分序列每個數都加上一;
兩種情況一加, 就得到轉移方程:i最大爲;
合併答案時,首先明白, 答案一定是兩種情況加起來。
枚舉第一種情況和爲j, 那麼另一種情況和爲n-j;兩者乘起來, 加到總方案中;即爲
代碼
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<cstdlib>
#include<ctime>
#include<cctype>
using namespace std;
int n, p;
int m;
int f[100010];
int g[400][100010]
int main()
{
cin >> n >> p;
cin >> m;
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 = 1; i <= n; i++)
{
int 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;
}
3.魔法 題目鏈接:省選難度
題目描述
C國有n座城市與m條有向道路組成, 城市和道路都從1開始編號, 經過i號道路需要的費用。
現在你要從1號城市出發去n號城市, 你可以施展最多k次魔法,使得通過下一條道路時, 需要的費用變爲原來的相反數,即費用從變成。請你算一算, 至少要花費多少費用才能完成這次旅程。
注意:使用魔法只是改變一次的花費, 而不改變一條道路自身的;最終的費用可以爲負,並且一個城市可以經過多次, 包括n號城市。
題目分析
有k次魔法可使用;
task 1: k = 0;
在一張權值爲正的圖上要使1點到n點最短, 很顯然, 跑最短路。
Floyd一波帶走~~
task 2: k = 1;
考慮怎樣從task1推到task2;
在改進的二維Floyd方程上加以改造;
設表示使用不超過k次魔法的情況下, 從i到j的最短路。
則當k = 1時,轉移思路就是不走和走了之間小的那個。枚舉唯一使用魔法的邊。
方程:
task 3: k = 2時
從k = 1推唄;
採用Floyd的本質思想, 枚舉一箇中轉點p。
i -> p最多用1次魔法, p -> j 最多用一次魔法
於是這樣就合成答案了;
方程長這樣子:
把min忽略, 有沒有一點像矩陣乘法的樣子, 只不過把乘換加, 加換min而已了。。
task 4:k > 2時
矩陣快速冪。。。。。。。
代碼
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<cstdlib>
#include<ctime>
#include<cctype>
using namespace std;
int f[110][110];
int n, m, k;
struct edge
{
int u, v, w;
}ed[2510];
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 <= m; i++)
{
int x, y, z;
cin >> x >> y >> z;
ed[i].u = x;
ed[i].v = y;
ed[i].w = z;
f[x][y] = z;
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
for(int k = 1; k <= n; k++)
{
f[j][k] = min(f[j][k], f[j][i] + f[i][k]);
}
}
}
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;
}
本次寫的代碼, 沒有經過評測, 不一定對哦~~