6.9模拟赛
6.9 模拟赛
T1 叠虚
并不知道叠虚是什么意思,有人来教教吗/kk
没想到解法,只觉得这题搜索和DP都做不了,所以用堆维护,贪心了一下,没证明,贪错了(((
正解给的是按力量值s和重量值w之和从小到大排序,排完序之后的序列就是从上至下的叠放顺序
证明:
\[取原序列中某连续两人的风险值r_i和r_{i+1}\\
有r_i=\sum_{j=1}^{i-1}w_j-s_i\\r_{i+1}=\sum_{j=1}^iw_j-s_{i+1}\\
令A=\sum_{j=1}^{i-1}w_j\\
则r_i=A-s_i\\r_{i+1}=A+w_i-s_{i+1}\\
若将两人调换位置,两人的风险值为r_i'和r’_{i+1}\\
有r'_{i+1}=A-s_{i+1}\\r'_i=A+w_{i+1}-s_{i}\\
假设s_i+w_i\geq s_{i+1}+w_{i+1}\\
即w_i-s_{i+1}\geq w_{i+1}-s_i\\
所以r_{i+1}\geq r'_i\\
又因为r_i\leq r'_i,r_{i+1}\geq r'_{i+1}\\
所以r_{i+1}\geq r_i\\
调换前,r_{i+1}为最大风险值\\
调换后,r'_i和r'_{i+1}都比r_{i+1}小\\
显然调换后序列更优
\]
码:(原来错误的码改的,丑死了)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
using namespace std;
const int INF=0x3f3f3f3f,N=100010;
int n,ans=0;
struct node{
int w,s,ss;//ss是w和s的和
}op[N];
bool cmp(node a,node b){
return a.ss<b.ss;//按ss排序
}
inline int read(){
int x=0,y=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*y;
}
int main(){
n=read();
for(int i=1;i<=n;i++) op[i].w=read(),op[i].s=read(),op[i].ss=op[i].w+op[i].s;
sort(op+1,op+1+n,cmp);
int pos=-op[1].s;
ans=pos;
for(int i=2;i<=n;i++){
pos=(op[i-1].s+op[i-1].w+pos)-op[i].s;
ans=max(ans,pos);
}
printf("%d\n",ans);
return 0;
}
T2 盖房子
听说是luogu原题,没找着
答案具有单调性,可以二分答案,判断的时候以行和列为单位判就可以了
开longlong麻烦开全谢谢
码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
using namespace std;
const int INF=0x3f3f3f3f,N=1010;
ll mapp[N][N];
ll n,m,ans=0,maxx=0,minn=0x3f3f3f3ff,s[N];
bool vis[N][N];//一列一行
inline ll read(){
ll x=0,y=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*y;
}
bool check(ll x){//我就这一个地方没开ll,卡掉50pts
memset(vis,false,sizeof(vis));
int top;
for(int i=1;i<=n;i++){
top=0;
for(int j=1;j<=m;j++) if(mapp[i][j]>=x) s[top++]=j;
for(int j=0;j<top;j++){
for(int k=j+1;k<top;k++){
if(vis[s[j]][s[k]]) return true;
vis[s[j]][s[k]]=true;
}
}
}
return false;
}
ll bisearch(){
ll l=minn,r=maxx;
while(l<r){
ll mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
return l;
}
int main(){
n=read(),m=read();
if(n<2||m<2) cout<<0<<endl,exit(0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
mapp[i][j]=read(),maxx=max(maxx,mapp[i][j]),minn=min(minn,mapp[i][j]);
printf("%lld\n",bisearch());
return 0;
}
T3 矿脉
这个词我会(
6e11可过
题解给的是DP做法\(O(n^4)\)
我的是3e8过了
考虑维护两个四维数组,先定左上点求最大,再以左上点为转移求答案,式子在代码里面
题解:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
using namespace std;
const int INF=0x3f3f3f3f,N=55;
int dp[N][N][N][N],n,m,s[N][N],q;
int f[N][N][N][N];
inline int read(){
int x=0,y=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*y;
}
int main(){
n=read(),m=read(),q=read();
memset(f,0,sizeof f);
memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) s[i][j]=read();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
s[i][j]+=(s[i-1][j]+s[i][j-1]-s[i-1][j-1]);
}
}//二维前缀和
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int l=1;l<=n;l++){
for(int r=1;r<=n;r++){
//这里
dp[i][j][l][r]=s[l][r]-s[i-1][r]-s[l][j-1]+s[i-1][j-1];
if(l-1>=i) dp[i][j][l][r]=max(dp[i][j][l][r],dp[i][j][l-1][r]);
if(r-1>=j) dp[i][j][l][r]=max(dp[i][j][l][r],dp[i][j][l][r-1]);
//这里
}
}
}
}
for(int i=n;i>=1;i--){
for(int j=n;j>=1;j--){
for(int l=1;l<=n;l++){
for(int r=1;r<=n;r++){
//这里
f[i][j][l][r]=dp[i][j][l][r];
if(i+1<=l) f[i][j][l][r]=max(f[i][j][l][r],f[i+1][j][l][r]);
if(j+1<=r) f[i][j][l][r]=max(f[i][j][l][r],f[i][j+1][l][r]);
//这里
}
}
}
}
for(int i=1;i<=q;i++){
int a,b,c,d;
a=read(),b=read(),c=read(),d=read();
printf("%d\n",f[a][b][c][d]);
}
return 0;
}
我哒:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
using namespace std;
const int INF=0x3f3f3f3f,N=60;
int m,n,q,s[N][N];
struct query{
int x1,y1,x2,y2,ans;
}qo[100010];
inline int read(){
int x=0,y=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*y;
}
void work(){
int f[N][N];
for(int d=1;d<=q;d++){
for(int i=qo[d].y1;i<=qo[d].y2;i++)
for(int j=i;j<=qo[d].y2;j++){
int sum=0;
for(int k=qo[d].x1;k<=qo[d].x2;k++){
if(sum>0) sum+=s[k][j]-s[k][i-1];
else sum=s[k][j]-s[k][i-1];
qo[d].ans=max(qo[d].ans,sum);
}
}
}
}
int main(){
n=read(),m=read(),q=read();
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) s[i][j]=read(),s[i][j]+=s[i][j-1];
for(int i=1;i<=q;i++) qo[i].x1=read(),qo[i].y1=read(),qo[i].x2=read(),qo[i].y2=read(),qo[i].ans=-INF;
work();
for(int i=1;i<=q;i++) printf("%d\n",qo[i].ans);
return 0;
}
T4 食堂承包
离谱的搜索,本来看着像多背包,但是数据不对劲而且价值都是1,以为搜索拿不了多少分然后就没写,然后XiEn1847随机化贪心拿了50pts,Orz
搜索好题,几乎所有剪枝都用上了
答案具有单调性可以二分答案
直接DFS上
剪枝:
- 对于一个二分出来的答案,我们选择体积更小的装入
- 二分上下界:上界:保证总容量能够包了体积最小的r个物品 下界:保证体积最小的l个物品装进容量最大的l个背包里
- 把价格相同的餐厅一起考虑
- 各种可行性剪枝
码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
using namespace std;
const int INF=0x3f3f3f3f,N=900010;
int n,m,mon[N],w[N],smon=0,l,r,sw[N];
bool cmp(int a,int b){
return a>b;
}
inline int read(){
int x=0,y=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*y;
}
bool dfs(int x,int pre,int v,int tot){
if(!x) return 1;
if(smon-v<sw[tot]) return 0;
int tmp=((w[x]==w[x+1])?pre:1);
for(int i=tmp;i<=n;i++){
if(mon[i]>=w[x]){
mon[i]-=w[x];
int t=((mon[i]<w[1])?mon[i]:0);
if(dfs(x-1,i,v+t,tot)){
mon[i]+=w[x];
return 1;
}
mon[i]+=w[x];
}
}
return 0;
}
bool check(int x){
return dfs(x,1,0,x);
}
int main(){
n=read();
for(int i=1;i<=n;i++) mon[i]=read(),smon+=mon[i];
m=read();
for(int i=1;i<=m;i++) w[i]=read();
sort(w+1,w+1+m);
sort(mon+1,mon+n+1,cmp);
for(int i=1;i<=m;i++) sw[i]=sw[i-1]+w[i];
l=0,r=m;
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
return 0;
}