题面
题目顺序为BCDA
B.叠虚
T 2 贪 错 了
sum-=(kyon[i].w);
不能一起减掉kyon[i].s啊,sum里存的是重量和,减掉了就相当于当前的牛的力量对后面的牛有影响,显然不对,感谢XiEn1847巨佬的指正qwq- 可能减着减着成负数了,所以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
- vector存的话输出可能会奇奇怪怪?
可能是我写挂了 - 用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
- 注意赋初值,赋极小值
- 注意各种正序倒序枚举
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后,声名大振
一直在努力思考多背包问题怎么做结果这题是暴搜?!
- 关于最多承包多少餐厅,用二分答案
- 关于暴搜:一看这个数据范围 (N<=10000,M<=400000) 就十分不可做,所以我们需要各种剪枝
当然剪枝是玄学我们不能知道它剪到什么程度,但是终归是能快不少的- 排序:商人从大到小,餐厅从小到大 因为餐厅尽量承包便宜的显然更好,商人钱越多可能承包的餐厅数越多显然也更好,这样枚举的时候更容易找到可行解
- 二分的上下界:上界:商人的资金总量>=餐厅的总价&&最有钱的商人>=最贵的餐厅
下界:当前商人的资金<=当前的餐厅价格,他承包不起 - 最优性剪枝:我们注意到数据范围 (M<=400000,w_i<=128) ,所以有很多餐厅价格相同。对于价格相同的餐厅,枚举的商人单调,可以减少搜索
- 可行性剪枝:若当前商人的资金<最便宜的餐厅,则这些钱就没用了,必然无解。通过这一点可优化二分的上下界
- 用人工栈防止爆栈
其实大概不用也可的?
- 注意数组的大小
因为数组开小了挂了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