小白月赛90 :D、E

文章讲述了在编程竞赛中,作者遇到的线段覆盖问题,通过深度优先搜索(DFS)解决线段组合覆盖所有点的问题,同时提到如何通过优先队列优化算法以降低时间复杂度,最终实现最小工作量完成任务。
摘要由CSDN通过智能技术生成

D:小A的线段(dfs)

蒟蒻赛时写的dfs超时了,以此记录一下自己错误的思路>>

可以看到本题m的数据范围在10以内,所以我们可以通过dfs来枚举出所有线段的组合情况,而要判断我们选择的线段是否能将所有的点覆盖至少两次.可以通过差分数组求前缀来判断..那么是否会超时呢?我们知道深搜时间复杂度是2^n(2种状态,最大深度为10),10个线段就是1024,然后判断数组需要O(n),也就是10的五次方,O(m*n) = 1024 * 10 ^ 5不超1e9 可以接受.

思路:

对于每一条线段,我们有选或者不选两种状态,那么设置一个use数组判断这个线段选了没有.然后通过构造差分数组来对左右端点操作.

代码:

/* */
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const int MAX = 1e5+7;
int n,m,ans,l[15],r[15],c[MAX];
int use[15];
void dfs(int now)
{
	if(now == m+1)
	{
		//如果m条线段选完了
		memset(c,0,sizeof c);//差分初始化
		for(int i=1;i<=m;i++)
		{
			if(use[i])
			{
				//观察他的状态
				c[l[i]]++;
				c[r[i]+1]--; 
			}
		}
		int flag = 1;
		for(int i=1;i<=n;i++)
		{
			c[i]+=c[i-1];
			if(c[i] < 2)flag = 0;
		}
		if(flag)ans++;//全都符合 
		return;
	}
	
	/*-重难点-*/
	//在可以枚举所有可能的情况下 一个一个来 毕竟深搜就是要搜出所有可能的情况,所以每一个点都要把所有情况包含在内
	for(int i=0;i<=1;i++)
	{
		//0表示不选 1表示选 -- 两种可能的状态
		use[now] = i;
		dfs(now+1);
		use[now] = 0; 
	} 
	
}
void solve()
/*算数开ll 存图用int*/
{
	
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	cin>>l[i]>>r[i];
	dfs(1);//从第一个开始搜
	cout<<ans; 
}/*算数开ll 存图用int*/
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);
    cout.tie(0);
    //cout<<fixed<<setprecision(2);
    int t=1;
    //cin>>t;
    while(t--)
      solve();
    return 0;
} 

下次对状态的判断不能错了┭┮﹏┭┮

E:小A的任务(枚举 + 优先队列)

题目摘要:A的任务占一排,B的任务占一排,如果要完成某个B任务,那么需要先完成与之对应的A任务。现在要完成一定数量的B任务,求最小的工作量。

分析:目的是完成k个B任务,所以最少是在完成k个A任务的情况下完成才有k个B任务去让你完成,然而我们可以多完成一些A任务,然后在所以可以完成的B任务里面挑选k个最小的,所以本体思路就来了:枚举完成的A任务的任务数量,(可以在完成k个A任务前提下完成k个B任务,完成(k+1)个A任务前提下完成k个B任务...)然后将b任务放在优先队列里,每次够k个的时候取一下最小值。

但是如果我的优先队列是小根堆,每次取最小值,那么当k很大时,我需要遍历队列取出来,会达到O(n2)的复杂度。那么怎么来简化它呢?

定义两个变量一个sum1记录A的累加,一个sum2记录B的累加,每次当我的队列里面的元素大于k的时候,我把里面最大的那个去掉,同时sum2 - 最大的那个元素。然后当队列元素个数size=k时,取一下最小值,这样就O(1)的遍历了所有队列可能。

代码

/* */
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define int long long
#define LL _int128
const int INF = 0x3f3f3f3f;
const int MAX = 1e6+7;
#define pb push_back 
#define ff first
#define ss second
typedef pair<int,int>pai;
int a[MAX],b[MAX];
void solve()
/*算数开ll 存图改回int*/
{
	int n,k;cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)cin>>b[i];
    
    int x;
    while(k--){
        cin>>x;
        int ans = 1e18;//注意开long long
        int sum1=0,sum2=0;
        priority_queue<int,vector<int>,less<int>>q;//大根堆
         for(int i=1;i<=n;i++){
            sum1+=a[i];
            sum2+=b[i];
            q.push(b[i]);
            if(q.size() > x){
                //现在把最大的那个(最不优的解)抛去
                sum2 -= q.top();
                q.pop();
            }
            if(q.size() == x){
                //sum2是一组解
                ans = min(ans,sum1+sum2);
            }
        } 
        cout<<ans<<endl;
    }
    
	
}/*算数开ll 存图改回int*/
signed main()
{
	ios::sync_with_stdio(false);cin.tie(0);
    cout.tie(0);
    //cout<<fixed<<setprecision(2);
    int t=1;
    //cin>>t;
    while(t--)
      solve();
    return 0;
} 
  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值