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;
}
posted @ 2022-09-15 16:34  wzx_believer  阅读(25)  评论(0编辑  收藏  举报