1244A Pens and Pencils
題意:給定a,b,c,d,k。有兩種物品,一個物品1的價值是c,物品2的價值是d。揹包容量至多可以裝k個物品。問有沒有裝物品的方案使得物品1的價值至少位a,物品2的價值至少位b。有輸出合法的一種,沒有輸出-1.
題解:直接求
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
int a,b,c,d,k;
cin>>t;
while(t--){
cin >> a >> b >> c >> d >> k;
int pen = a / c + (a % c != 0);
int pencil = b / d + (b % d != 0);
if(pen + pencil > k){
puts("-1");
}else{
cout<<pen<<" "<<pencil<<endl;
}
}
return 0;
}
1244B Rooms and Staircases
題意:Nikolay 有個兩層樓的房子,每層n個房間。每個房間和左右相鄰房間有個門可以互通。一層和二層有一些梯子。有梯子的不同層房間可以互通。如下圖。問Nikolay從任一房間開始,每次可以走到互通的房間中去,能到達的最大房間數。期間不能進入一個房子兩次。
題解:貪心。如果沒有梯子,顯然數目就是n。如果有一個梯子,那麼就是拐一個彎,如下圖。
如果有大於等於兩個梯子。不難看出中間的可以不予考慮,最大的依然是考慮邊緣的兩個梯子,從一端開始,拐最長的彎。
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t, n;
string s, s1;
cin >> t;
while(t--){
cin >> n >> s;
s1 = s;
reverse(s1.begin(), s1.end());
string::size_type pos1 = s.find('1');
string::size_type pos2 = s1.find('1');
if(pos1 == s.npos) cout << n << endl;
else cout<< 2 * n - 2 * min(pos1, pos2) << endl;
}
return 0;
}
1244C The Football Season
題意:給定n,p,w,d
求解滿足下式的非負整數x,y,z
題解:
相當於求
如果有一組滿足,
則
且,即有一組和更小的且的滿足條件。所以我們只要從0開始到枚舉即可
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n, p, w, d;
cin >> n >> p >> w >> d;
long long x = -1,y;
for(int i = 0;i < w; ++i){
long long r = p - d * i;
if(r < 0) break;
if(r % w) continue;
y = i;
x = r / w;
break;
}
if(x == -1 || x + y > n) puts("-1");
else cout<<x<<" "<<y<<" "<<n - x - y<<endl;
return 0;
}
1244D Paint the Tree
題意:給定一棵樹n個點,每個點可以着三種顏色。每個點着每種顏色都有一個cost(共3n個cost)。求樹的最小cost着色方案,需要滿足相鄰的三個點的顏色不同。如果不存在方案,輸出-1
題解:首先,如果有一個點的度數大於等於3,那是不可能有合法方案的。所以存在合法方案的一定是一條鏈。對於一條鏈的情況,着色方案由前兩個點完全確定。所以我們可以枚舉前兩個點的6種着色方案,求最小cost方案。代碼寫挫了。稍後改
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+3;
int d[N], C[3][N], C2[3][N], color[N];
int id[N];
vector<int> edge[N];
void dfs(int x, int fa, int depth){
id[depth] = x;
for(int i = 0;i < edge[x].size(); ++i){
int u = edge[x][i];
if(vis[u]) continue;
dfs(u, x, depth + 1);
}
}
int get_next_color(int col1, int col2){
bool col_meet[3] = {0};
col_meet[col1] = col_meet[col2] = true;
for(int i = 0;i < 3; ++i) if(!col_meet[i]) return i;
}
int main(){
int n;
cin >> n;
memset(d, 0, (n + 1) * sizeof(int));
for(int _ = 0; _ < 3; ++_)
for(int i = 1;i <= n; ++i) scanf("%d",&C[_][i]);
int ans = 0, root = -1;
for(int i = 1;i < n; ++i){
int u,v;
cin >> u >> v;
++d[u];
++d[v];
edge[u].push_back(v);
edge[v].push_back(u);
if(d[u] > 2 || d[v] > 2) ans = -1;
}
for(int i = 1;i <= n; ++i){
if(d[i] == 1){
root = i;
break;
}
}
if(ans == -1) puts("-1");
else{
if(n == 1){
int min_c = min(C[0][1],min(C[1][1],C[2][1]));
if(min_c == C[0][1]) puts("1");
else if(min_c == C[1][1]) puts("2");
else puts("3");
return 0;
}
dfs(root, -1, 0);
for(int i = 1; i <= n; ++i){
for(int j = 0;j < 3; ++j)
C2[j][i] = C[j][id[i - 1]];
}
long long min_v = 1e18;
int col1 = -1, col2 = -1;
for(int c = 0; c < 9; ++c){
int p = c % 3, pp = (c / 3) % 3;
if(p == pp) continue;
long long cur_v = C2[p][2] + C2[pp][1];
for(int idx = 3; idx <= n; ++idx){
int k = get_next_color(p, pp);
cur_v += C2[k][idx];
pp = p, p = k;
}
if(min_v > cur_v){
min_v = cur_v;
col1 = c % 3;
col2 = (c / 3) % 3;
}
}
color[id[0]] = col2, color[id[1]] = col1;
int p = col1, pp = col2;
for(int i = 2; i < n; ++i){
int k = get_next_color(p, pp);
color[id[i]] = k;
pp = p, p = k;
}
cout<<min_v<<endl;
for(int i = 1;i <= n; ++i) cout<<color[i] + 1<<" ";
cout<<endl;
}
return 0;
}
1244E Minimizing Difference
題意:給定一個正整數數組,長度爲n,和一個整數k
你可以每次對數組中的一個元素進行加1或者減1的操作,最多執行k次。問經過一系列操作後數組的最大值和最小值的差最小是多少。
題解:貪心。先將數組排序。首先要使得差值減小,一定將兩邊的往中間移動。 所以我們先從兩端開始移動。將i個數移動一次的代價是i。
假設我們將前個數移動到了區間中,那麼倒數個數也要移動到區間中。不然的話,不妨假設只移動末尾個數。我們移動個數只需要j個操作,而移動前面的i個數要i個操作。將前i個數進行回退,操作後j個數,不會產生更壞的結果。
所以,我們只需要枚舉移動到的位置,將兩端個數進行移動,一直到不能再移動。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 3;
long long a[N];
int main() {
long long n,k;
cin >> n >> k;
for(int i = 1;i <= n; ++i) cin >> a[i];
sort(a + 1, a + n + 1);
long long ans = 0;
for(int i = 1;i <= n / 2; ++i){
int j = n - i + 1;
long long r = (a[i + 1] - a[i] + a[j] - a[j - 1]) * i;
if(r <= k) k -= r;
else{
ans = a[j] - a[i] - k / i;
break;
}
}
cout<<ans<<endl;
return 0;
}
1244F Chips
題意:給一個環,每個節點一開始爲黑色或者白色。每一輪,每個節點的顏色根據它旁邊的兩個節點以及它自己的顏色進行改變。如果有兩個黑色,它就變成黑色。兩個白色就變成白色。問k輪以後每個點都是什麼顏色。
題解:注意如果兩個顏色相同的連在一起,那麼它們的顏色就不會再發生變化。我們只需要只考慮如下兩種情況,其他的都可以等同於這兩種
第一個的話,接下來幾輪的變化如下
第二個的話,接下來幾輪的變化如下
可以看到每一輪,連續的那部分都在往周圍擴張,每次長度爲1.所以對於每個點,我們只需要求和它距離最近的那個連續段。如果距離<= k,那麼它就變成最近連續段的那種顏色。如果距離>k。那麼當k爲奇數時,顏色進行翻轉,否則不變。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 1;
bool fixed_pos[N];
int left_dis[N], right_dis[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,k;
string s;
cin >> n >> k >> s;
for(int i = 0;i < n; ++i){
if(s[i] == s[(i - 1 + n) % n] || s[i] == s[(i + 1) % n])
fixed_pos[i] = true, left_dis[i] = right_dis[i] = INT_MAX;
}
for(int i = 0;i < n; ++i){
if(!fixed_pos[i]) continue;
left_dis[i] = right_dis[i] = 0;
for(int j = (i + 1) % n; !fixed_pos[j]; j = (j + 1) % n)
left_dis[j] = left_dis[(j - 1 + n) % n] + 1;
for(int j = (i + n - 1) % n; !fixed_pos[j]; j = (j + n - 1) % n)
right_dis[j] = right_dis[(j + 1) % n] + 1;
}
for(int i = 0; i < n; ++i){
if(min(left_dis[i], right_dis[i]) > k){
bool cur_char_is_w = (s[i] == 'W') ^ (k & 1);
cout<<(cur_char_is_w ? 'W' : 'B');
}else{
int pos = (i - left_dis[i] + n) % n;
//不連續段wbwb或者bwbw一定是偶數的,所以不會產生left_dis[i] == right_dis[i]這種情況
if(left_dis[i] > right_dis[i])
pos = (i + right_dis[i]) % n;
cout<<s[pos];
}
}
return 0;
}
1244G Running in Pairs
題意:有兩組人,共2n個參加比賽。每場兩個人,每組第個人完成比賽的時間是。每場比賽的時間是兩個選手中用時較長的。你可以給每組的選手調整順序,讓比較時間變長。 給定一個k,要求時間不超過k的最長比賽時間,以及對應的每組人員順序。
題解:因爲是兩兩比賽,所以我們可以先固定第一組的順序爲1,2,…,n。然後調整第二組即可。最小的比賽時間是所以當小於這個數時,就不存在方案。當k大於等於這個數時,就存在。最大的比賽時間是什麼,就是當第二組倒序的時候,記爲。
在最小比賽和最大比賽時間中的任何時間都是可達的(可以通過一種順序得到)。
我們可以從排列開始,將最小的往後移動,移動1步變成時間比大1,移動到變成時,和比大。
接下來移動2,可以看到當2的位置在2以後的時候開始每次增1,一直到2移動到n - 1的位置。產生的和的增加可以從1到n - 1 - 2
當移動i的時候,產生的和的增加數可以用從1一直到n - 2 * i + 1 (當n - 2i + 1 <= 0時,就不能再增加了,即已經移動超過一半,前面部分的順序無關緊要)。比如
前面的次序怎麼改都和最大的一樣。
所以我們能夠每次把一個數往後移動,一直到k或者最大時間。
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n,k;
cin >> n >> k;
if(n * (n + 1) / 2 > k) {puts("-1"); return 0;}
long long tot_sum = n * (n + 1) / 2;
k -= tot_sum;
int pos;
for(pos = 1;pos < n; ++pos){
if(n - 2 * pos + 1 <= 0) continue;
if(n - 2 * pos + 1 <= k){
tot_sum += n - 2 * pos + 1;
k -= n - 2 * pos + 1;
}else{
break;
}
}
if(pos >= n)
cout << tot_sum <<endl;
else
cout << tot_sum + k<<endl;
for(int i = 1; i <= n; ++i) cout<<i<<" "; cout<<endl;
if(pos >= n){
for(int i = n; i >= 1; --i) cout<<i<<" "; cout<<endl;
}else{
if(k > 0){
//前面正序的一段
for(int i = pos + 1; i <= 2 * pos + k - 1; ++i) cout<<i<<" ";
//pos的位置
cout<<pos<<" ";
for(int i = 2 * pos + k; i <= n; ++i) cout<<i<<" ";
//後面倒序的一段
for(int i = pos - 1; i >= 1; --i) cout<<i<<" ";
cout<<endl;
}else{
for(int i = pos; i <= n; ++i) cout<<i<<" ";
for(int i = pos - 1; i >= 1; --i) cout<<i<<" ";
cout<<endl;
}
}
return 0;
}