\(D1T1\) 引水入城 \((OK)\)
\(D1T2\) 关押罪犯 \((OK)\)
\(D1T3\) 机器翻译 \((OK)\)
\(D1T4\) 乌龟棋 \((OK)\)
这年的一些题目之前都做过好多遍了,所以做得还是比较快的,个人认为最难的是\(T1???\)
\(T1\)要分析出性质:在一个合法方案中(记住这个前提),每个蓄水厂\((1,j)\),覆盖到的沙漠城市一定会是一段连续的区间\([l_j,r_j]\).这个吧,想到了就很显然,想不到吧就\(GG\)了.
简单证明一下,反证法,如果在一个方案中,有一个蓄水厂\((1,j)\),它覆盖到的沙漠城市是\([l_j,r_j]\)和\([l_{j'},r_{j'}]\)这两段,那么\([r_j+1,l_{j'}-1]\)这一段覆盖不到,肯定是因为它比周围都要高,那么也就是说无论如何这一段都不可能被覆盖得到,即这种方案不合法(这种图是无解的).
因为数据范围不大,所以我们可以\(dfs\)预处理出每个蓄水厂\((1,j)\)能覆盖到的区间\([l_j,r_j]\).
于是题目就转换为了给你\(m\)个区间,选出最少的区间覆盖线段\([1,m]\).就是贪心模板题了.还是因为数据范围很小,所以都不用考虑什么\(O(n)\)贪心,直接\(n^2\)贪心即可.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=505;
int n,m,bj,ans;
int h[N][N],l[N][N],r[N][N],visit[N][N];
int dx[4]={0,0,1,-1},
dy[4]={1,-1,0,0};
inline void dfs(int x,int y){
visit[x][y]=1;
for(int i=0;i<4;++i){
int xx=x+dx[i],yy=y+dy[i];
if(xx<1||xx>n||yy<1||yy>m)continue;
if(h[xx][yy]>=h[x][y])continue;
if(!visit[xx][yy])dfs(xx,yy);//!!!!!这里卡了好久,这个点如果之前搜过,虽然不能继续搜下去,但它可以更新其它的点
l[x][y]=min(l[x][y],l[xx][yy]);
r[x][y]=max(r[x][y],r[xx][yy]);
}
}
int main(){
n=read();m=read();memset(l,0x3f,sizeof(l));
for(int j=1;j<=m;++j)l[n][j]=r[n][j]=j;
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)h[i][j]=read();
for(int j=1;j<=m;++j)if(!visit[1][j])dfs(1,j);
for(int j=1;j<=m;++j)if(!visit[n][j])++ans,bj=1;
if(bj){printf("0\n%d\n",ans);return 0;}
int L=1,R=0;
while(L<=m&&R<m){
for(int j=1;j<=m;++j)
if(l[1][j]<=L&&r[1][j]>=L)R=max(R,r[1][j]);
++ans;L=R+1;
}
printf("1\n%d\n",ans);
return 0;
}
\(T2\)这道题学并查集的时候做过,学二分图染色的时候又做过.个人认为本题如果选用二分+二分图染色的话,思维难度较低,如果用扩展域并查集的话,代码简洁.博客
\(T3\)我爱\(STL!!!\)这题开个双端队列\(deque\),然后依据题意模拟即可.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
deque<int>q;int in[1005];
int main(){
int m=read(),n=read(),ans=0;
for(int i=1;i<=n;++i){
int x=read();
if(!in[x]){
if((int)q.size()==m)in[q.front()]=0,q.pop_front();
++ans;q.push_back(x);in[x]=1;
}
}
printf("%d\n",ans);
return 0;
}
\(T4\)做过很多次了,因为只有\(4\)种牌,每种牌的数量又不超过\(40\),所以可以直接放进状态里,设\(f[now][a][b][c][d]\)表示当前走到了第\(now\)个格子,还剩\(a,b,c,d\)这么多牌时的最大得分.
采用记忆化搜索,转移就只有\(4\)种,很显然.
然后交一发,发现编译失败\(???\)额,数组太大了,可以把第一维直接删掉.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=351;
int n,m;
int val[N],sum[5],f[41][41][41][41];
inline int dfs(int now,int a,int b,int c,int d){
if(now>=n)return 0;
if(f[a][b][c][d])return f[a][b][c][d];
int cnt=0;
if(a)cnt=max(cnt,val[now+1]+dfs(now+1,a-1,b,c,d));
if(b)cnt=max(cnt,val[now+2]+dfs(now+2,a,b-1,c,d));
if(c)cnt=max(cnt,val[now+3]+dfs(now+3,a,b,c-1,d));
if(d)cnt=max(cnt,val[now+4]+dfs(now+4,a,b,c,d-1));
return f[a][b][c][d]=cnt;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;++i)val[i]=read();
for(int i=1;i<=m;++i)++sum[read()];
printf("%d\n",val[1]+dfs(1,sum[1],sum[2],sum[3],sum[4]));
return 0;
}