10.10水题鉴赏
菜鸡只会做这种题/kk
T1 Birthday
题目大意
有\(n\)个物品\(m\)元钱,每个物品有三个参数\(W_i,A_i,B_i\),分别表示该物品价格、价值以及首次购买的额外价值,求用这\(m\)元钱能得到的物品的最大价值和。
\(1\leq n\leq 1000\)
\(1\leq m,W_i,A_i,B_i\leq 2000\)
Sol
显然是一个完全背包问题。但是有一个首次购买的特殊情况,所以在完全背包枚举物品时分是否购买该物品\(dp\),最后取最值即可。
Code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
const int maxn=2010;
int f[maxn][2],a[maxn],b[maxn],w[maxn];
int n,m;
int main()
{
freopen("birthday.in","r",stdin);
freopen("birthday.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)w[i]=read(),a[i]=read(),b[i]=read();
for(int i=1;i<=n;i++)
{
for(int j=w[i];j<=m;j++)
{
f[j][1]=max(f[j][1],f[j-w[i]][0]+a[i]+b[i]);
f[j][1]=max(f[j][1],f[j-w[i]][1]+a[i]);
}
for(int j=0;j<=m;j++)f[j][0]=max(f[j][0],f[j][1]);
}
printf("%d\n",f[m][0]);
return 0;
}
T2 chess
题目大意
给定一个\(n*m\)的棋盘,给定每个位置能否放置棋子,放置棋子时不能有相邻棋子,求所有合法摆放方案。
\(1\leq n,m\leq 12\)
Sol
这个数据范围就显然状压\(dp\)了,属于最基础版本的状态转移。首先枚举每一行的状态,存入所有合法状态。然后依次枚举相邻两行间的状态,上下不冲突就转移,数据范围很小,直接暴力枚举即可。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=100000000;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
int f[1<<20][20],zt[20],dp[1<<20][20],len[20];
int n,m,lim;
signed main()
{
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
n=read();m=read();
lim=(1<<m)-1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<m;j++)
{
int x=read();
if(!x)zt[i]|=(1<<j);
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=lim;j++)
{
if(j&zt[i])continue;
if(j&(j<<1))continue;
dp[++len[i]][i]=j;
}
}
for(int i=1;i<=len[1];i++)f[dp[i][1]][1]=1;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=len[i-1];j++)
{
for(int k=1;k<=len[i];k++)
{
if((dp[k][i]&dp[j][i-1])==0)
{
f[dp[k][i]][i]=(f[dp[k][i]][i]+f[dp[j][i-1]][i-1])%p;
}
}
}
}
int ans=0;
for(int i=0;i<=lim;i++)ans=(ans+f[i][n])%p;
cout<<ans<<endl;
}
T3 Question
题目大意
给定一个由\(1\)和\(0\)组成的\(n*m\)的矩阵,求全部由\(1\)构成的最大子矩阵面积。
\(1\leq n,m\leq 1000\)
Sol
四个题里面最难的了吧算是……
暴力枚举每一行每一列,根据当前数字维护当前该列状态,单调栈维护以该列为最矮的高度的矩阵最大宽度,左右各扫一遍即可。维护\(ans=max(ans,h[j]*(maxx[j]+1))\)。
Code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
const int maxn=1010;
int a[maxn][maxn],n,m,ans;
int h[maxn],maxx[maxn],st[maxn];
int main()
{
freopen("question.in","r",stdin);
freopen("question.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
a[i][j]=read();
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i][j])h[j]++;
else h[j]=0;
maxx[j]=0;
}
int top=0;
for(int j=1;j<=m+1;j++)
{
while(top&&h[j]<h[st[top]])
{
maxx[st[top]]+=j-st[top]-1;
top--;
}
st[++top]=j;
}
top=0;
for(int j=m;j>=0;j--)
{
while(top&&h[j]<h[st[top]])
{
maxx[st[top]]+=st[top]-j-1;
top--;
}
st[++top]=j;
}
for(int j=1;j<=m;j++)ans=max(ans,h[j]*(maxx[j]+1));
}
cout<<ans<<endl;
return 0;
}
road
题目大意
给定一个\(n\)个点\(m\)条边带点权不带边权的\(DAG\),求从入度为\(0\)的点到出度为\(0\)的点的最长路。
\(1 ≤ n ≤ 100000, 0 ≤ m ≤ 2000000 \\ 0 ≤ |Vi| ≤ 20000\)
Sol
\(DAG\)求最值,显然拓扑。每个点直接维护最值即可,然后如果是出度为\(0\)就更新答案。小心一下负数的情况即可。
Code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
return f?x:-x;
}
const int maxn=100010,maxm=2000010;
struct edge
{
int to,next;
}e[maxm];
int h[maxn],ei;
inline void add(int x,int y)
{
e[++ei]=(edge){y,h[x]};
h[x]=ei;return;
}
int deg[maxn],ans[maxn],a[maxn];
int n,m,rst=-1e9,d[maxn];
queue<int>qu;
int main()
{
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
deg[y]++;add(x,y);
d[x]++;
}
memset(ans,-0x3f,sizeof(ans));
for(int i=1;i<=n;i++)
{
if(!deg[i])ans[i]=0,qu.push(i);
}
while(!qu.empty())
{
int x=qu.front();qu.pop();
ans[x]+=a[x];
if(!d[x])rst=max(rst,ans[x]);
for(int i=h[x];i;i=e[i].next)
{
int to=e[i].to;
ans[to]=max(ans[to],ans[x]);
deg[to]--;if(!deg[to])qu.push(to);
}
}
cout<<rst<<endl;
}
总结
这套题每道都签到,但是机房依然有非常厉害的同学意外爆零。因为简单,所以也没有用心造数据取调细节,所幸我都考虑到了。这四个题的样例非常水,这也导致该同学\(T4\)写\(dijkstra\)都没能卡掉。基础练练手还是很不错的。至少能减少在正式考试的时候出现签到题分拿不稳的情况。

浙公网安备 33010602011771号