zoukankan      html  css  js  c++  java
  • 2021.06.09模拟赛

    题面
    题目顺序为BCDA

    B.叠虚

    T 2 贪 错 了

    1. sum-=(kyon[i].w);不能一起减掉kyon[i].s啊,sum里存的是重量和,减掉了就相当于当前的牛的力量对后面的牛有影响,显然不对,感谢XiEn1847巨佬的指正qwq
    2. 可能减着减着成负数了,所以ans初值要赋一个极小的负数

    code:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define maxn 100010
    #define ll long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    const int inf=2147483646;
    ll n,sum,ans=-inf;//可能会减出负数? 
    struct data{
    	ll w;
    	ll s;
    }kyon[maxn];
    
    bool cmp(data x,data y){
    	return x.w+x.s>y.w+y.s;
    }
    
    int main(){
    	read(n);
    	for(int i=1;i<=n;i++){
    		read(kyon[i].w),read(kyon[i].s);
    		sum+=kyon[i].w;
    	} 
    	sort(kyon+1,kyon+n+1,cmp);
    	for(int i=1;i<=n;i++){
    		sum-=(kyon[i].w);
    		ans=max(ans,sum-kyon[i].s);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    /*
    3
    10 3
    2 5
    3 3
    */
    //2
    

    C.盖房子

    开始写了暴力但是发现可以二分答案,于是写了正解/nice

    1. vector存的话输出可能会奇奇怪怪?可能是我写挂了
    2. 用flag[][]打一个神奇的标记,我们不断加入一行判断是不是和前面的重复了,相当于不断加入数对,看能不能构成矩形

    code:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    #define maxn 1010
    #define ll long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    ll n,m,mp[maxn][maxn],tmp;
    bool flag[maxn][maxn];
    //vector<ll> v;
    int v[maxn],cnt;
    
    bool check(ll x){
    	memset(flag,0,sizeof(flag));
    	for(int i=1;i<=n;i++){
    		int cnt=0;
    		for(int j=1;j<=m;j++){
    			if(mp[i][j]>=x) v[++cnt]=j; 
    		}
    		for(int j=1;j<=cnt-1;j++){
    			for(int k=j+1;k<=cnt;k++){
    				if(flag[v[j]][v[k]]) return 1;
    				else flag[v[j]][v[k]]=1;
    			}
    		}
    	}
    	return 0;
    }
    
    int main(){
    	read(n),read(m);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			read(mp[i][j]);
    			tmp=max(tmp,mp[i][j]);
    		}
    	}
    	ll l=0,r=tmp;
    	while(l<r){
    		ll mid=(l+r+1)/2;
    		if(check(mid)) l=mid;
    		else r=mid-1;
    	}
    	cout<<l<<endl;
    	return 0;
    }
    /*
    3 3
    1 2 3
    4 5 6
    7 8 9
    */
    //5
    /*
    4 4
    0 1 1 0
    0 0 0 0 
    1 1 0 1
    1 1 1 0 
    */
    //1
    /*
    3 4
    3 7 9 4
    2 5 8 10
    4 20 8 7
    */
    //7
    

    D.矿脉

    一眼最大子矩阵和,应该能拿部分分但是有地方写挂了所以只拿了部分分
    正解是dp

    1. 注意赋初值,赋极小值
    2. 注意各种正序倒序枚举

    code:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define maxn 60
    #define ll long long
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    int n,m,q,mp[maxn][maxn],x1,y1,x2,y2,sum[maxn][maxn],tmp;
    int f[maxn][maxn][maxn][maxn],g[maxn][maxn][maxn][maxn];
    
    int maxx(int a,int b,int c){
    	int res=max(a,max(b,c));
    	return res;
    }
    
    void dp(){
    	memset(f,-0x3f,sizeof(f));
    	memset(g,-0x3f,sizeof(g));
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			for(int p=i;p<=n;p++){
    				for(int q=j;q<=m;q++){
    					tmp=sum[p][q]-(sum[p][j-1]+sum[i-1][q])+sum[i-1][j-1];
    					f[i][j][p][q]=maxx(tmp,f[i][j][p-1][q],f[i][j][p][q-1]);
    				}
    			}
    		}
    	}
    	for(int i=n;i;i--){//倒序枚举! 
    		for(int j=m;j;j--){//倒序枚举! 
    			for(int p=i;p<=n;p++){//正序枚举! 
    				for(int q=j;q<=m;q++){//正序枚举! 
    					g[i][j][p][q]=maxx(f[i][j][p][q],g[i+1][j][p][q],g[i][j+1][p][q]);
    				}
    			}
    		}
    	}
    }
    
    int main(){
    	read(n),read(m),read(q);
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			read(mp[i][j]);
    			sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1])+mp[i][j];
    		}
    	}
    	dp();
    	for(int i=1;i<=q;i++){
    		read(x1),read(y1),read(x2),read(y2);
    		cout<<g[x1][y1][x2][y2]<<endl;
    	}
    	return 0;
    }
    /*
    3 3 2
    1 2 1
    -1 0 -1
    2 1 0
    1 1 3 2
    2 1 2 1
    */
    //5
    //-1
    /*
    1 1 1
    1
    1 1 1 1
    */
    //1
    /*
    3 3 2
    -2 -5 -10
    -1 -4 -9
    -3 -100 -6
    1 1 3 3
    2 1 3 3
    */
    //-1
    //-1
    

    A.食堂承包

    题意:(Hs)_(black)(jiazhaopeng) 在AK完IOI2020后,声名大振
    一直在努力思考多背包问题怎么做结果这题是暴搜?!

    1. 关于最多承包多少餐厅,用二分答案
    2. 关于暴搜:一看这个数据范围 (N<=10000,M<=400000) 就十分不可做,所以我们需要各种剪枝 当然剪枝是玄学我们不能知道它剪到什么程度,但是终归是能快不少的
      • 排序:商人从大到小,餐厅从小到大 因为餐厅尽量承包便宜的显然更好,商人钱越多可能承包的餐厅数越多显然也更好,这样枚举的时候更容易找到可行解
      • 二分的上下界:上界:商人的资金总量>=餐厅的总价&&最有钱的商人>=最贵的餐厅
        下界:当前商人的资金<=当前的餐厅价格,他承包不起
      • 最优性剪枝:我们注意到数据范围 (M<=400000,w_i<=128) ,所以有很多餐厅价格相同。对于价格相同的餐厅,枚举的商人单调,可以减少搜索
      • 可行性剪枝:若当前商人的资金<最便宜的餐厅,则这些钱就没用了,必然无解。通过这一点可优化二分的上下界
      • 用人工栈防止爆栈其实大概不用也可的?
    3. 注意数组的大小因为数组开小了挂了40pts调了半个小时的孩子如是说

    code:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define maxn 10010
    #define maxm 400010
    using namespace std;
    template<typename T>
    inline void read(T &x){
    	x=0;bool flag=0;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
    	for(;isdigit(c);c=getchar()) x=x*10+(c^48);
    	if(flag) x=-x;
    }
    
    int n,a[maxn],m,b[maxm];//数组开小了!!!!! 
    int suma,sumb[maxm],tot,ans;
    
    bool cmp(int x,int y){
    	return x>y;
    }
    
    bool dfs(int x,int pre,int v){
    	if(!x) return 1;
    	if(suma-v<sumb[tot]) return 0;
    	int tmp=((b[x]==b[x+1])?pre:1);
    	for(int i=tmp;i<=n;i++){
    		if(a[i]>=b[x]){
    			a[i]-=b[x];
    			int t=((a[i]<b[1])?a[i]:0);
    			if(dfs(x-1,i,v+t)){
    				a[i]+=b[x];
    				return 1;
    			}
    			a[i]+=b[x];
    		}
    	}
    	return 0;
    }
    
    bool check(int x){
    	tot=x;
    	return (dfs(x,1,0));
    }
    
    int main(){
    	read(n);
    	for(int i=1;i<=n;i++) read(a[i]),suma+=a[i];
    	read(m);
    	for(int i=1;i<=m;i++) read(b[i]);
    	sort(a+1,a+n+1,cmp);
    	sort(b+1,b+m+1);
    	for(int i=1;i<=m;i++) sumb[i]=sumb[i-1]+b[i];
    	int l=0,r=m;
    	while(l<=r){
    		int mid=(l+r)/2;
    		if(check(mid)) ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    /*
    2
    20
    10
    4
    8
    7
    10
    9
    */
    //3
    
  • 相关阅读:
    2021.1.30 刷题(滑动窗口最大值-单调队列)
    2021.1.30 刷题(括号匹配)
    2021.1.29 刷题(重复的子字符串-KMP实现)
    2021.1.28 刷题(栈、队列)
    2021.1.27 刷题(KMP字符串匹配)
    2021.1.26 学习KMP算法
    2021.1.25 刷题(四数之和)
    2021.1.24 刷题(三数之和-哈希表)
    2021.1.23 刷题(快乐数-哈希表)
    2021.1.22 刷题(用数组实现哈希表)
  • 原文地址:https://www.cnblogs.com/DReamLion/p/14867741.html
Copyright © 2011-2022 走看看