• CF 958E2. Guard Duty (medium)


    这道题是昨天linkfqy dalao上课讲的一道题

    当时他讲的时候就想到了一种玄学的搞法,然后不敢相信自己切掉了

    没想到后来CHJ dalao也想到了这种算法,然后发现是对的

    后来10min就切掉了

    看来以后要活跃一点了

    这里主要讲两种算法(想WQS二分这种高深的算法就不讲了)

    首先讲题意:一个人它只能在某个时间点去跟看k个他的客人对话,或是结束他和当前这个客人的对话。但是它不能在两个连续的时间段进行讲话,问它最少需要多少时间来和k个客人对话。

    1.堆

    我们先按时间排序,处理出每一个时间段的长度

    然后就建模转化成:n-1个点中取k个并且每两个都不相邻的最少代价和。

    这就和一道题很像了,看Luogu P1484&sol

    用堆水一水即可

    CODE

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int N=500005;
    int k,n,x[N],a[N],pre[N],nxt[N],ans;
    bool vis[N];
    struct data
    {
    	int x,num;
    	bool operator <(const data &s) const
    	{
    		return s.x<x;
    	}
    };
    priority_queue<data> small;
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch=tc();
    	while (ch<'0'||ch>'9') ch=tc();
    	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	register int i; read(k); read(n);
    	for (i=1;i<=n;++i)
    	read(x[i]); 
    	sort(x+1,x+n+1);
    	for (i=2;i<=n;++i)
    	{
    		a[i-1]=x[i]-x[i-1];
    		pre[i-1]=i-2; nxt[i-1]=i;
    		small.push((data){a[i-1],i-1});
    	}
    	a[0]=a[n]=1e9; pre[n]=n-1; nxt[0]=1;
    	while (k--)
    	{
    		while (vis[small.top().num]) small.pop();
    		data p=small.top(); small.pop(); ans+=p.x;
    		p.x=a[p.num]=a[pre[p.num]]+a[nxt[p.num]]-a[p.num];
    		vis[pre[p.num]]=vis[nxt[p.num]]=1;
    		pre[p.num]=pre[pre[p.num]]; nxt[pre[p.num]]=p.num; 
    		nxt[p.num]=nxt[nxt[p.num]]; pre[nxt[p.num]]=p.num;
    		small.push(p);
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    2.DP

    首先这种题目就是看一眼就想到DP的吧

    我们令f[i][j][0/1]表示前i个点中取j个并且第i个点取or不取(1表示取,2表示不取)的最少代价

    则很容易写出转移:

    • f[i][j][1]=f[i-1][j-1][0]+a[i]

    • f[i][j][0]=min(f[i-1][j][0],f[i-1][j][1])

    首先我们发现i的那一维可以滚动存储优化掉

    然后空间就够了,但是时间为O(nk)

    然后坚信CF的机子是无敌的满怀信仰地交上去说不定就过了

    然后这里我们就要讲一个结论了:

    无论怎么取,都只可能取到3*k小的数

    Why?很简单,如果所有的数左右都取,算上它自己,也就3个,扩展开就是3*k

    所以我们就把时间优化到了O(k^2)

    CODE

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=500005,K=5005;
    int x[N],a[N],n,k,f[2][K][2],pre,num,tot;
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch=tc();
    	while (ch<'0'||ch>'9') ch=tc();
    	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    }
    inline int min(int a,int b)
    {
    	return a<b?a:b;
    }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	register int i,j;
    	read(k); read(n);
    	for (i=1;i<=n;++i)
    	read(x[i]); sort(x+1,x+n+1);
    	for (i=2;i<=n;++i)
    	x[i-1]=a[i-1]=x[i]-x[i-1];
    	sort(x+1,x+n); num=3*k<n?x[3*k]:x[n-1]; 
    	memset(f[0],63,sizeof(f[0])); f[0][0][0]=f[0][0][1]=0;
    	int now=0,lst=1,pre=-1;
    	for (i=1;i<n;++i)
    	{
    		if (a[i]>num) continue;
    		now^=1; lst^=1; memset(f[now],63,sizeof(f[now])); 
    		++tot;
    		if (i-1!=pre) 
    		{
    			for (j=1;j<=min(tot,k);++j)
    			{
    				f[now][0][0]=f[now][0][1]=0;
    				f[now][j][1]=min(f[lst][j-1][0],f[lst][j-1][1])+a[i];
    				f[now][j][0]=min(f[lst][j][0],f[lst][j][1]);
    			} 
    		} else
    		{
    			for (j=1;j<=min(tot,k);++j)
    			{
    				f[now][0][0]=f[now][0][1]=0;
    				f[now][j][1]=f[lst][j-1][0]+a[i];
    				f[now][j][0]=min(f[lst][j][0],f[lst][j][1]);
    			}
    		} pre=i;
    	}
    	printf("%d",min(f[tot&1][k][0],f[tot&1][k][1]));
    	return 0;
    }
    
  • 相关阅读:
    2012 里SQL Server Data Tools进程处理数据库时 怎么没有更改设置呢?
    如何重启数据库服务
    如何更改sql server登陆密码
    备份和还原数据库
    MySQL查询优化之性能提升一个数量级
    浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析之后续补充说明(有图有真相)
    浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析(好戏在后面,有图有真相)
    模板在二叉树和队列中的应用(借助队列广度遍历二叉树)
    SSH公钥登录且禁止密码登录及更改默认端口
    springmvc+druid+dataSource配置的两种方式
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9057054.html
Copyright © 2020-2023  润新知