題目1
題目鏈接
題目大意:
有這樣的一個(gè)字符串,如圖:
現(xiàn)在光標(biāo)停留在最左邊的數(shù)字1處,我們可以進(jìn)行以下的操作:
1、將當(dāng)前光標(biāo)所在位置的數(shù)字輸出;
2、移動光標(biāo)到相鄰的數(shù)字,比如說從1移動到2,從2移動到3;(1的左邊不能移動,0的右邊不能移動)
現(xiàn)在想知道,輸出特定的4個(gè)字符,最少需要幾次操作。
輸入:
第一行,整數(shù)?? 表示t個(gè)樣例 ?? (1≤??≤10000)
每個(gè)樣例一行,4個(gè)整數(shù);
輸出:
每個(gè)樣例一行,輸出最少的操作次數(shù)。
Examples
input
10
1111
1236
1010
1920
9273
0000
7492
8543
0294
8361
output
4
9
31
27
28
13
25
16
33
24
題目解析:
輸出數(shù)字的最小操作方法:
1、將光標(biāo)移到指定位置;
2、展示當(dāng)前數(shù)字;
題目的意思非常簡單,但是如果直接通過if去實(shí)現(xiàn),在計(jì)算0的位置時(shí),會比較繁瑣;(因?yàn)閿?shù)字0在最右邊,破壞了字符和數(shù)字的對應(yīng)關(guān)系)
這里有個(gè)實(shí)現(xiàn)的小技巧,我們將數(shù)字0看成10,那么整個(gè)數(shù)字序列就是從小到大。
這樣在計(jì)算操作1的時(shí)候,就能通過數(shù)字相減直接得到結(jié)果。
class Solution {
static const int N = 201010;
int a[N];
public:
void solve() {
int t;
cin >> t;
while (t--) {
char s[10];
cin >> s;
int cur = 1, ans = 4;
for (int i = 0; i < 4; ++i) {
int idx = s[i] - '0';
if (idx == 0) idx += 10;
ans += abs(cur - idx);
cur = idx;
}
cout << ans << endl;
}
}
}
ac;
題目2
題目鏈接
題目大意:
給出一個(gè)長度為n的字符串s,現(xiàn)在需要移除字符串中的k個(gè)字符,剩下的字符可以隨意排列;
問,剩下的字符能否組成一個(gè)回文串?
輸入:
第一行,整數(shù)?? 表示t個(gè)樣例 ?? (1≤??≤10000)
每個(gè)樣例2行
第一行,?? and ?? (0≤??<??≤1e5)
第二行,字符串s;(小寫字母組成)
輸出:
每個(gè)樣例一行,如果有解則輸出YES;如果無解則輸出NO;
Examples
input
14
1 0
a
2 0
ab
2 1
ba
3 1
abb
3 2
abc
6 2
bacacd
6 2
fagbza
6 2
zwaafa
7 2
taagaak
14 3
ttrraakkttoorr
5 3
debdb
5 4
ecadc
5 3
debca
5 3
abaac
output
YES
NO
YES
YES
YES
YES
NO
NO
YES
YES
YES
YES
NO
YES
題目解析:
最終的字符串可以任意調(diào)整順序,那么重點(diǎn)在于每個(gè)字符的數(shù)量;
題目是要求構(gòu)造回文串,如果某個(gè)字符數(shù)量是偶數(shù),那么可以組成回文串;如果某個(gè)字符數(shù)量是奇數(shù),那可能會導(dǎo)致無法構(gòu)成回文串。
假設(shè)統(tǒng)計(jì)所有字符的數(shù)量,有x個(gè)偶數(shù)字符,有y個(gè)奇數(shù)字符;那么能構(gòu)成回文串的條件就是y<=1;(如果只有1個(gè)奇數(shù),可以把多出來這個(gè)字符放在回文串中間)
由于題目增加了一個(gè)限制,要去除k個(gè)字符,那么奇數(shù)字符就可以有更多(因?yàn)橐瞥龝r(shí)優(yōu)先移除奇數(shù)字符),所以最終條件是y<=1+k;
class Solution {
static const int N = 201010;
public:
void solve() {
int t;
cin >> t;
while (t--) {
int n, k;
string s;
cin >> n >> k;
cin >> s;
vector<int> cnt(26);
for (int i = 0; i < n; ++i) cnt[s[i] - 'a']++;
int y = 0;
for (int i = 0; i < 26; ++i) y += (cnt[i] % 2);
if (y <= 1 + k) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
}
ac;
題目3
題目鏈接
題目大意:
有n個(gè)整數(shù)的數(shù)組a,再給出整數(shù)k;
現(xiàn)在可以進(jìn)行任意次以下操作:
選擇某個(gè)數(shù)組元素,將該元素+1;
現(xiàn)在要求數(shù)組最終的乘積,能夠整除數(shù)字k,問最少需要操作多少次;
輸入:
第一行,整數(shù)?? 表示t個(gè)樣例 ?? (1≤??≤10000)
每個(gè)樣例2行
第一行,?? and ?? (2≤??≤1e5 , 2≤??≤5 )
第二行,n個(gè)整數(shù)??1,??2,…,????(1≤????≤10).
輸出:
每個(gè)樣例一行,輸出最小的操作次數(shù)。
Examples
input
15
2 5
7 3
3 3
7 4 1
5 2
9 7 7 3 9
5 5
5 4 1 2 3
7 4
9 5 1 5 9 5 1
3 4
6 3 6
3 4
6 1 5
3 4
1 5 9
4 4
1 4 1 1
3 4
3 5 3
4 5
8 9 9 3
2 5
1 6
2 5
10 10
4 5
1 6 1 1
2 5
7 7
output
2
2
1
0
2
0
1
2
0
1
1
4
0
4
3
題目解析:
第一反應(yīng),是將k進(jìn)行因數(shù)分解,然后將數(shù)字分配到對應(yīng)因數(shù),還要考慮一個(gè)數(shù)字對應(yīng)多個(gè)因數(shù)的情況(比如說a1=25,可以對應(yīng)到兩個(gè)因數(shù)5);
這樣題目整體處理會比較麻煩。
考慮到k的數(shù)據(jù)范圍很小,因數(shù)情況也只有4=2x2的可能,可以不使用這種方法來處理。
假如k=2時(shí),如果數(shù)組a存在偶數(shù),則ans=0,否則ans=1;
假如k=3時(shí),判斷每個(gè)數(shù)組元素與3的余數(shù)即可,如果有能整除,則ans=0,否則為ans=3-最大余數(shù);
假如k=4時(shí),按照2的因數(shù)來算,比如說4就算2個(gè),如果數(shù)組中存在2個(gè),那么ans=0;如果數(shù)組中存在1個(gè),那么ans=1(因?yàn)楸厝贿€有一個(gè)奇數(shù),這個(gè)奇數(shù)可以+1得到偶數(shù));如果數(shù)組中存在0個(gè),那么ans=2(因?yàn)橛袃蓚€(gè)數(shù)字,必定是2個(gè)奇數(shù));(同時(shí)也可以按照k=3的做法,計(jì)算下如果累加每個(gè)數(shù)字的情況,取較小者)
假如k=5時(shí),可以按照k=3的做法來;
class Solution {
static const int N = 201010;
public:
void solve() {
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
int ans = 0, cnt = 0;
while (n--) {
int tmp;
cin >> tmp;
if (tmp % k) ans = max(ans, tmp % k);
else ans = k;
while (tmp % 2 == 0) {
++cnt;
tmp /= 2;
}
}
if (k == 4) {
if (cnt >= 2) cout << 0 << endl;
else cout << min(k - ans, 2 - cnt) << endl;
}
else {
cout << (k - ans) << endl;
}
}
}
}
ac;
題目4
題目鏈接
題目大意:
有n個(gè)整數(shù)的數(shù)組??1,??2,…,????
現(xiàn)在可以對數(shù)組進(jìn)行多次下述操作:
選擇數(shù)組中的某個(gè)整數(shù)????,如果????<0 則可以把????替換為[????,0]區(qū)間中的整數(shù);如果????>0,則可以把????替換為 [0,????] 區(qū)間中的整數(shù).
現(xiàn)在想知道最少需要多少次操作,才能使得最終數(shù)組所有數(shù)字的乘積盡可能的小;
輸入:
第一行,整數(shù)?? 表示t個(gè)樣例 ?? (1≤??≤500)
每個(gè)樣例2行
第一行,整數(shù)??(1≤??≤100)
第二行,n個(gè)整數(shù)??1,??2,…,????(?1e9≤????≤1e9).
輸出:
每個(gè)樣例第一行,輸出需要的操作次數(shù)x;
接下來每個(gè)操作一行,輸出參數(shù)i和x,表示將a[i]設(shè)置為x;
Examples
input
4
1
155
4
2 8 -1 3
4
-1 0 -2 -5
4
-15 -75 -25 -30
output
1
1 0
0
0
1
3 0
題目解析:
題目的要求的是乘積盡可能小,如果數(shù)組當(dāng)前乘積結(jié)果是正數(shù),那么將任意一個(gè)數(shù)字變?yōu)?,結(jié)果就是最小值0;
如果當(dāng)前乘積是數(shù)字0,那么不管如何修改,最終結(jié)果都是0;
如果當(dāng)前乘積是數(shù)字負(fù)數(shù),那么修改任何數(shù)字,都可能會讓結(jié)果更大,而不是更小。
思路想清楚,代碼就很簡單了。
class Solution {
static const int N = 201010;
int a[N];
public:
void solve() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
int zero = 0, cnt = 0;
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) {
if (a[i] == 0) ++zero;
else if (a[i] < 0) ++cnt;
}
if (cnt % 2 || zero) cout << 0 << endl;
else cout << "1\n 1 0" << endl;
}
}
}
ac;
題目5
題目鏈接
題目大意:
有n個(gè)整數(shù)區(qū)間[??1,??1],[??2,??2],…,[????,????],每個(gè)區(qū)間有一個(gè)系數(shù) ????,每個(gè)區(qū)間的重量為?????(?????????).
現(xiàn)在可以進(jìn)行下列操作:
1、任意調(diào)換n個(gè)整數(shù)區(qū)間,左區(qū)間l的數(shù)字;
2、任意調(diào)換n個(gè)整數(shù)區(qū)間,右區(qū)間r的數(shù)字;
3、任意調(diào)換n個(gè)整數(shù)區(qū)間,系數(shù)c的數(shù)字;
要求調(diào)換完,每個(gè)區(qū)間 [????, ????] 都滿足 ???? < ????;
輸入:
第一行,整數(shù)?? 表示t個(gè)樣例 ?? (1≤??≤10000)
每個(gè)樣例有4行
第一行,整數(shù) ?? (1≤??≤2e5)
第二行,整數(shù) ??1,??2,…,????(1≤????≤2?1e5)
第三行,整數(shù) ??1,??2,…,???? (????<????≤2?1e5)
第四行,整數(shù)??1,??2,…,???? (1≤????≤1e7 )
題目保證,{??1,??2,…,????,??1,??2,…,????}數(shù)字各不相同。
輸出:
每個(gè)樣例一行,輸出調(diào)整后,所有區(qū)間的最小重量和。
Examples
input
2
2
8 3
12 23
100 100
4
20 1 2 5
30 4 3 10
2 3 2 3
output
2400
42
題目解析:
題目允許對l、r、c進(jìn)行任意調(diào)換,那么可以先對l、r、c進(jìn)行從小到大排序,便于分析問題。
接下來做數(shù)據(jù)分析,先考慮n=2的情況
a1 a2
b1 b2
c1 c2
正常情況下 (b1-a1)c1 + (b2 - a2)c2
先考慮c1和c2的情況,假設(shè)b1-a1和b2-a2相等,那么調(diào)換c1和c2對于結(jié)果沒有影響。
假設(shè)b1-a1<b2-a2呢? 這次c1<c2就會導(dǎo)致更小的結(jié)果,應(yīng)該把更大的數(shù)字放在前面。
延伸分析,假設(shè)有若干個(gè)區(qū)間長度,那么可以將區(qū)間長度從小到大排序,接著把c越大的匹配到越前面的結(jié)果。
接下來,就是怎么匹配出來若干個(gè)區(qū)間長度,并且保證結(jié)果符合要求。
首先還是從2個(gè)區(qū)間開始分析
a1 a2
b1 b2
我們知道最終兩個(gè)區(qū)間的和,不管如何交換,必然是 (b1 - a1) + (b2 - a2)。
那在這種情況下,我們讓其中一個(gè)區(qū)間盡可能小,另外一個(gè)區(qū)間必然會增大。
沿著這個(gè)貪心思路,我們只需要每次讓右區(qū)間,去匹配到盡可能接近的左區(qū)間。
有一個(gè)簡單實(shí)現(xiàn)方案,對于每一個(gè)左區(qū)間(從大到小開始),我們從大到小去遍歷右區(qū)間,找出來一個(gè)最近的節(jié)點(diǎn)。
但是這樣的復(fù)雜度是O(N x N);
我們可以引入優(yōu)先隊(duì)列來簡化操作,在選擇右區(qū)間的時(shí)候,big隊(duì)列表示前面選擇過的節(jié)點(diǎn)隊(duì)列,從大到小排列,這樣就可以直接從big隊(duì)列中找到之前遍歷過的最大值;
backup表示還沒有被選擇過的節(jié)點(diǎn),從小到大排列,這樣當(dāng)big隊(duì)列最大值都無法滿足要求,就需要從backup中取數(shù)字。
這樣整體的復(fù)雜度就可以降低到NlogN的級別。
class Solution {
static const int N = 201010;
priority_queue<int> big;
priority_queue<int, vector<int>, greater<int> > backup;
int a[N], b[N], c[N], diff[N];
public:
void solve() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) cin >> b[i];
for (int i = 0; i < n; ++i) cin >> c[i];
sort(a, a + n);
sort(c, c + n);
for (int i = 0; i < n; ++i) backup.push(b[i]);
for (int i = n - 1; i >= 0; --i) {
int cur = 0;
if (!big.empty()) {
cur = big.top();
}
if (cur < a[i]) { // 當(dāng)前big堆里不滿足
while (cur < a[i]) {
cur = backup.top();
big.push(cur);
backup.pop();
}
big.pop();
}
else {
big.pop();
while (big.size() && big.top() > a[i]) {
backup.push(cur);
cur = big.top();
big.pop();
}
}
diff[i] = cur - a[i];
}
sort(diff, diff + n);
lld ans = 0;
for (int i = 0; i < n; ++i) ans += diff[i] * 1LL * c[n - 1 - i];
cout << ans << endl;
}
}
}
ac;