zoukankan      html  css  js  c++  java
  • 题解 CF1203 F1,F2 Complete the Projects

    12.03 在我心目中一直是一个特别的数字呢 QwQ

    比赛链接

    CF1203F1 Complete the Projects (easy version)

    题目链接

    暴力做法,是直接枚举全排列。如果想要DP,则必须记录之前已经用过了哪些项目,复杂度势必大于(2^n)。故考虑挖掘题目的性质,尝试贪心。如果我们能贪心地求出一个最优(最有可能有解)的排列方式,则直接按照这种排列方式排好序后模拟一遍,就能判断答案是YES或NO了。

    我们把项目,按照(b_igeq 0)(b_i<0)分为两类。显然,应该先完成所有(b_igeq 0)的项目。于是问题变为两类项目内部应该分别如何排序。

    对于(b_igeq 0)的项目,把它们按(a_i)从小到大排序。这是因为,随着这类项目的进行,(r)的值单调不降,所以把一个(a_i)大的项目排在(a_i)小的项目前面是没有意义的(交换以后不会变劣)。

    对于(b_i<0)的项目。首先,为了满足任何项目结束后(r)非负,我们令(a_i=max(a_i,-b_i))。于是接下来只需要考虑每个项目开始前(rgeq a_i)这个条件。

    我们考虑两个项目((a_i,b_i))((a_j,b_j))(b_i,b_j<0)),在何种情况下,把(i)排在(j)前面更优。

    • 如果把(i)排在(j)前,则需要满足:(rgeq a_i), (r+b_igeq a_j) 。即:(rgeq max(a_i,a_j-b_i))
    • 如果把(j)排在(i)前,则需要满足:(rgeq a_j), (r+b_jgeq a_i) 。即:(rgeq max(a_j,a_i-b_j))

    若要使把(i)排在(j)前面更优(更有可能有解),则:(max(a_i,a_j-b_i)leq max(a_j,a_i-b_j))

    因为(b_i,b_j<0),所以(a_j-b_i>a_j)。所以后半部分的最大值一定不能取(a_j),即:(a_j<a_i-b_j (1))

    同时,要使得(max(a_i,a_j-b_i)leq a_i-b_j),即:(a_ileq a_i-b_j (2)), (a_j-b_ileq a_i-b_j (3))

    因为(b_j<0),所以((2))式恒成立。由((1),(3))得:(a_i+b_igeq a_j+b_j)

    于是我们得出结论,把(i)排在(j)前面更优,当且仅当:(a_i+b_igeq a_j+b_j)。也就是说,对于所有(b_k<0)的二元组((a_k,b_k)),我们将它们按((a_k+b_k))的值从大到小排列即可。

    时间复杂度(O(nlog n))

    参考代码(片段):

    const int MAXN=100;
    int n,cur,cnt_po,cnt_ne;
    struct node{
    	int a,b;
    }po[MAXN+5],ne[MAXN+5];
    bool cmp_po(node x,node y){return x.a<y.a;}
    bool cmp_ne(node x,node y){return x.a+x.b>y.a+y.b;}
    int main() {
    	read(n);read(cur);
    	for(int i=1;i<=n;++i){
    		int a,b;read(a);read(b);
    		if(b<0){
    			++cnt_ne;
    			ne[cnt_ne].a=max(a,-b);
    			ne[cnt_ne].b=b;
    		}
    		else{
    			++cnt_po;
    			po[cnt_po].a=a;
    			po[cnt_po].b=b;
    		}
    	}
    	sort(po+1,po+cnt_po+1,cmp_po);
    	sort(ne+1,ne+cnt_ne+1,cmp_ne);
    	for(int i=1;i<=cnt_po;++i){
    		if(cur<po[i].a){puts("NO");return 0;}
    		cur+=po[i].b;
    	}
    	for(int i=1;i<=cnt_ne;++i){
    		if(cur<ne[i].a){puts("NO");return 0;}
    		cur+=ne[i].b;
    	}
    	puts("YES");
    	return 0;
    }
    

    CF1203F2 Complete the Projects (hard version)

    题目链接

    根据上一题中的分析,我们仍然可以把所有项目,按(b_igeq 0)(b_i<0)分为两类。且所有第一类项目排在所有第二类项目前面。

    对于(b_igeq0)的项目,我们只需要把它们按(a_i)从小到大排序后,贪心地能选则选。因为选了当前项目,能使答案增加,且(r)不会减少。

    对于(b_i<0)的项目,我们仍然把它们按照((a_i+b_i))从大到小排列。现在相当于从中选出一个子序列(不用改变选出的元素的顺序),使其合法。

    考虑DP。设(dp[i][j])表示考虑了(排序后的)前(i)个((b_k<0)的)项目,完成第(i)个项目后,rating为(j),此条件下,最多做了多少个项目。转移分为不做当前项目,和做当前项目两种情况。即:

    [dp[i][j]=max(dp[i-1][j],dp[i-1][j-b_i]+1) ]

    时间复杂度(O(nlog n+nm))。其中(m)表示整个过程中的最大rating,(mleq r+nbleq 60000)

    参考代码:

    //problem:CF1203F2
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    namespace Fread{
    const int MAXN=1<<20;
    char buf[MAXN],*S,*T;
    inline char getchar(){
    	if(S==T){
    		T=(S=buf)+fread(buf,1,MAXN,stdin);
    		if(S==T)return EOF;
    	}
    	return *S++;
    }
    }//namespace Fread
    #ifdef ONLINE_JUDGE
    	#define getchar Fread::getchar
    #endif
    template<typename T>inline void read(T& x){
    	x=0;int f=1;
    	char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))x=x*10+(ch-'0'),ch=getchar();
    	x*=f;
    }
    /*  ------  by:duyi  ------  */ // myt天下第一
    const int MAXN=100,MAXR=30000;
    int n,cur,cnt_po,cnt_ne,ans,dp[MAXN+5][MAXR*2+5];
    struct node{
    	int a,b;
    }po[MAXN+5],ne[MAXN+5];
    bool cmp_po(node x,node y){return x.a<y.a;}
    bool cmp_ne(node x,node y){return x.a+x.b>y.a+y.b;}
    int main() {
    	read(n);read(cur);
    	for(int i=1;i<=n;++i){
    		int a,b;read(a);read(b);
    		if(b<0){
    			++cnt_ne;
    			ne[cnt_ne].a=max(a,-b);
    			ne[cnt_ne].b=b;
    		}
    		else{
    			++cnt_po;
    			po[cnt_po].a=a;
    			po[cnt_po].b=b;
    		}
    	}
    	sort(po+1,po+cnt_po+1,cmp_po);
    	sort(ne+1,ne+cnt_ne+1,cmp_ne);
    	for(int i=1;i<=cnt_po;++i){
    		if(cur<po[i].a)continue;
    		cur+=po[i].b;ans++;
    	}
    	assert(cur<=MAXR*2);
    	dp[0][cur]=ans;
    	for(int i=1;i<=cnt_ne;++i){
    		for(int j=0;j<=cur;++j){
    			dp[i][j]=dp[i-1][j];
    			if(j-ne[i].b<=cur&&j-ne[i].b>=ne[i].a){
    				dp[i][j]=max(dp[i][j],dp[i-1][j-ne[i].b]+1);
    			}
    		}
    	}
    	for(int i=0;i<=cur;++i)ans=max(ans,dp[cnt_ne][i]);
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    再谈 Devstack(Rocky)
    记一次性能测试与优化经历
    Nova rebuild for boot from volume issue
    OpenStack RPM Sample 解析
    [Cinder] 存储 Qos
    Octavia Rocky UDP 负载均衡功能试验
    Keepalived + LVS-NAT 实现高可用四层 TCP/UDP 负载均衡器
    LVS 四层 TCP/UDP 负载均衡器
    集群的定义以及类别定义
    对程序员又了解了一些
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12659433.html
Copyright © 2011-2022 走看看