码迷,mamicode.com
首页 > 其他好文 > 详细

2021.06.09模拟赛

时间:2021-06-10 17:45:35      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:暴搜   搜索   mem   题意   black   line   --   math   开始   

题面
题目顺序为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.06.09模拟赛

标签:暴搜   搜索   mem   题意   black   line   --   math   开始   

原文地址:https://www.cnblogs.com/DReamLion/p/14867741.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!