2022杭电多校3
B 1002 Boss Rush
题意:你有 n 个技能,这种技能是一种灼烧技能,在len[i]秒的时间内,每秒造成d[i][j] 的伤害(j -> [1, len[i]])。每个技能有t[i]的冷却时间,在冷却时间内不能放其他技能,每个技能只能释放一次,但是会继续灼伤怪物。怪物的血量为m,请问最少多少时间可以杀死怪物。
其实没必要记忆化搜索 直接枚举集合就好
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int N = 20 , M = 2e5 + 10;
int a[N], b[N], c[N];
int n, m, mid;
int t[N], d[N][M], s[N][M], len[N];
int f[500010];
int dfs(int state)
{
if(f[state] != -1) return f[state]; // 记忆画搜索,避免重复计算
int ans = 0;
int time = 0;
for(int i = 0 ; i < n ; i ++ )
{
if(state >> i & 1) time += t[i + 1]; // 累计目前所花费的时间总和
}
if(time > mid) return f[state] = 0; // 若时间已经大于二分的mid,则不能击杀怪物
for(int i = 0 ; i < n ; i ++ )
{
if(!(state >> i & 1)) // 找到某一个位置为0,表示这回合释放技能i
ans = max(s[i + 1][min(len[i + 1], mid - time)] + dfs(state | (1 << i)) , ans);
// 加入我们预处理好的伤害值,但是要和mid - time剩余时间取min,因为多出来的时间在二分时间内不合法
}
return f[state] = ans; // 记录伤害值
}
bool check()
{
for(int i = 0 ; i < 1 << n ; i ++ ) f[i] = -1; // 初始化dp数组
return dfs(0) >= m;
}
void solve()
{
cin >> n >> m;
int l = 0 , r = 0;
for(int i = 1 ; i <= n ; i ++ )
{
cin >> t[i] >> len[i];
r += max(t[i], len[i]); // r的范围最好小一些,考虑极限时间,因为dfs的过程中时间越大,搜索的时间越就,复杂度越高,不能直接取INF
for(int j = 1 ; j <= len[i] ; j ++ ) cin >> d[i][j] , s[i][j] = s[i][j - 1] + d[i][j]; // 预处理前缀和
}
int INF = r;
while(l < r)
{
mid = l + r >> 1;
if(check()) r = mid;
else l = mid + 1;
}
if(r >= INF) cout << -1 << endl;
else cout << r - 1 << endl; // 时间从0开始算,所以要整体-1
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0);
int T = 1;
cin >> T;
while(T -- ) solve();
return 0;
}
C 1003 Cyber Language
题意:给定一个有空格的字符串,把首字母变成大写并删除其他字符。
分析:直接模拟即可,记得读入空格并用getchar忽略换行符。
#include <sstream>
#include <iostream>
using namespace std;
int main()
{
int T;
cin >> T;
getchar();
while (T--)
{
string line;
getline(cin, line); // getline忽略空格
stringstream ssin(line);
string s;
while (ssin >> s) cout << (char) (s[0] + 'A' - 'a');
cout << '\n';
}
return 0;
}
I 1009 Package Delivery
题意:有n个快递,每个快递有送达时间和退还时间,是一段区间。每次你可以选择某一天拿快递,一天内可以拿无数次,但是你的小车只能装最多k个快递,请在取得所有快递的前提下 最小化取快递的次数。
分析:因为一天可以拿很多次,我们贪心的思路是尽量让小车装满而不是浪费小车的空间。
核心:假如你在第x天能够拿cnt个快递,如果cnt的快递不是k的倍数(小车内有空余),那么就需要多拿一些其他快递使得自己的快递是k的倍数,当然可能没有这么多快递。
如果考虑这个核心贪心思路呢?我们让每一个快递都在他快要被退回的那一天那,这个时候到货的快递数量一定是最多的,我们的选择也是最多的。假设我们在第x天,并且保证退还时间在x之前的快递都已经被拿走了,那么我们先计算出deadline(退还时间)在x的快递数量。看看是否是k的倍数,如果恰好是k的倍数我们就很开心,小车没有浪费空间。加入不是k的倍数,也就是说我们少了一些空间,那么我们贪心的拿走一些不是deadline但是已经到了的快递。但是哪种快递的优先级更高呢?那些最先被退还的快递我们要先拿走,这样我们一定保证了最优的解决方案,因为我们的宗旨是让快递一拖再拖直到deadline。
基本做法:先对快递进行排序,用一个小根堆当作仓库。枚举所有快递的deadline时间,对于deadline之前到货的快递我们要先放到仓库。每次统计deadline = 当前时间的快递数量,如果不足k的倍数用剩下的deadline小的快递来凑即可,具体看代码。
非常好的一道贪心
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, m, k;
void solve()
{
cin >> n >> k;
vector<PII> segs; // 所有快递的到货时间和退还时间
vector<int> ed; // deadline
priority_queue<int, vector<int> , greater<int> > q; // 仓库
for(int i = 1 ; i <= n ; i ++ )
{
int l, r;
cin >> l >> r;
segs.push_back({l, r});
ed.push_back(r);
}
sort(segs.begin(), segs.end()); // 左端点排序,方便快递入库
sort(ed.begin(), ed.end()); // 按时间顺序枚举所有deadline
int cur = 0 , ans = 0;
for(auto it : ed)
{
int cnt = 0 ;
while(cur < n && segs[cur].first <= it) q.push(segs[cur ++].second); // 快递入库
while(q.size() && q.top() == it) cnt ++ , q.pop(); //统计deadline = ed 的快递数
ans += cnt / k; // 需要用几次小车
if(cnt % k == 0) continue; // 小车是否有空间多余
int extra = k - cnt % k; // 计算多余的空间
while(q.size() && extra -- ) q.pop(); // 用仓库内的其他快递凑成一车
ans ++ ; // 再加凑好的一车
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
int T;
cin >> T;
while(T -- ) solve();
return 0;
}
K 1011 Taxi
题意:有一个哥们一直在打车,他有打车券,想要如何才能让打车券抵最多的钱。券的规则是这样,每一个位置都有一个坐标和权值x, y ,z ,可以抵消的钱 = min(两点距离,目的地的点权值)。给定了n个点,每次给定一个坐标,求如何才能让打车券抵最多的钱。(两点距离指哈密顿距离)
在此之前看一下类似的题目
Codeforces Round #798 (Div. 2) D
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int N = 1e5 + 10, INF = 1e18;
int n, m, a[N], b[N], c[N], d[N];
struct Node
{
int x, y, w;
bool operator < (const Node &t) const
{
return w < t.w;
}
}e[N];
int x, y, ans;
bool check(int mid)
{
int w = e[mid].w;
int maxv = -INF; // maxv表示[mid - r]的最大哈密顿距离。
maxv = max(maxv, x + y + a[mid]);
maxv = max(maxv, x - y + b[mid]);
maxv = max(maxv, -x + y + c[mid]);
maxv = max(maxv, - x - y + d[mid]);
ans = max(ans, min(w, maxv)); // 更新答案
return w <= maxv;
}
void solve()
{
cin >> n >> m;
for(int i = 1 ; i <= n ; i ++ )
{
int x, y, w;
cin >> x >> y >> w;
e[i] = {x, y, w};
}
sort(e + 1, e + n + 1); // 按照w排序
a[n + 1] = b[n + 1] = c[n + 1] = d[n + 1] = -INF;
for(int i = n ; i >= 1 ; i -- )
{ // 预处理四个最值
a[i] = max(a[i + 1], -e[i].x - e[i].y);
b[i] = max(b[i + 1], -e[i].x + e[i].y);
c[i] = max(c[i + 1], e[i].x - e[i].y);
d[i] = max(d[i + 1], e[i].x + e[i].y);
}
while(m -- )
{
cin >> x >> y;
int l = 1 , r = n;
ans = -INF;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
check(r);
cout << ans << endl;
}
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0);
int T = 1;
cin >> T;
while(T -- ) solve();
return 0;
}
L 1012 Two Permutations
题意:给定两个长度为n的排列a,b和一个由两个排列组成的长度为2*n的数组c,每次从两个排列中任意一个的最左端取出一个数放到c[i]里,请问构造出c数组的方案数。
分析:给出两种解法,一种是大家都在用的记忆化搜索dp,转移方程很简单:
if(a[x] == c[x + y - 1]) f[i][ty] += dfs(x + 1, y , 0)
if(b[y] == c[x + y - 1]) f[i][ty] += dfs(x , y + 1, 0)
dfs(x, y, ty)中x和y表示亟待匹配的坐标,递归时直接找是否相同即可。ty表示是从a转移还是从b转移。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int N = 1e6 + 10 , mod = 998244353;
int f[N][2]; // a/b即将匹配的位置
int a[N], b[N], c[N];
int n, m;
int dfs(int x, int y, int ty)
{
if(x > n && y > n) return 1;
if(f[x + y - 1][ty] != -1) return f[x + y - 1][ty];
int ans = 0;
if(a[x] == c[x + y - 1] && x <= n)
ans = (ans + dfs(x + 1, y, 0)) % mod;
if(b[y] == c[x + y - 1] && y <= n)
ans = (ans + dfs(x, y + 1, 1)) % mod;
f[x + y - 1][ty] = ans;
return ans;
}
void solve()
{
cin >> n;
for(int i = 1 ; i <= n ; i ++ ) cin >> a[i];
for(int i = 1 ; i <= n ; i ++ ) cin >> b[i];
for(int i = 1 ; i <= 2 * n ; i ++ ) cin >> c[i];
for(int i = 0 ; i <= 2 * n + 5; i ++ ) f[i][0] = f[i][1] = -1;
int ans = 0;
if(a[1] == c[1]) ans = (dfs(2, 1, 0) + ans) % mod;
if(b[1] == c[1]) ans = (dfs(1, 2, 1) + ans) % mod;
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0);
int T;
cin >> T;
while(T -- ) solve();
return 0;
}