一、给定一个操作序列,由四种字符组成,L代表向左,R代表向右,U代表向上,D代表向下。从(1,1)出发,中途如果走出矩阵界限或者走到障碍点,那么这个操作序列就是不合法的。要求的是最少删除几个操作可以使序列合法。
容易想到,我们可以把问题转化为到某个点我们最多可以走多少步。然后状态转移式就列出来了。f[k][i][j]表示递推到了操作序列的第k项,走到(i,j)最多可以走多少步。直接转移就好,注意在中间如果某个点是障碍点,把他赋值为0.
当时考场写的比较麻烦一些,可以自己写一下转移。
#include<bits/stdc++.h>
using namespace std;
const int N=510;
int n,m,k;
int f[2][N][N];bool ff[2][N][N];
char s[N],a[N][N];
int main(){
freopen("island.in","r",stdin);
freopen("island.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;++i) scanf("%s",a[i]+1);
scanf("%s",s+1);
f[0][1][1]=0;ff[0][1][1]=1;
for(int now=1;now<=k;++now){
for(int i=1;i<=min(now+1,n);++i)
for(int j=1;j<=min(now+1,m);++j){
if(i+j-2>now) continue;
f[now%2][i][j]=f[(now%2)^1][i][j];
ff[now%2][i][j]=ff[(now%2)^1][i][j];
if(a[i][j]=='1'){
f[now%2][i][j]=0;
ff[now%2][i][j]=0;continue;
}else{
if(s[now]=='D'&&ff[now%2^1][i-1][j]){
f[now%2][i][j]=max(f[now%2][i][j],f[(now%2)^1][i-1][j]+1);
ff[now%2][i][j]=max(ff[now%2][i][j],ff[(now%2)^1][i-1][j]);
}else if(s[now]=='U'&&ff[now%2^1][i+1][j]){
f[now%2][i][j]=max(f[now%2][i][j],f[(now%2)^1][i+1][j]+1);
ff[now%2][i][j]=max(ff[now%2][i][j],ff[(now%2)^1][i+1][j]);
}else if(s[now]=='L'&&ff[now%2^1][i][j+1]){
f[now%2][i][j]=max(f[now%2][i][j],f[(now%2)^1][i][j+1]+1);
ff[now%2][i][j]=max(ff[now%2][i][j],ff[(now%2)^1][i][j+1]);
}else if(s[now]=='R'&&ff[now%2^1][i][j-1]){
f[now%2][i][j]=max(f[now%2][i][j],f[(now%2)^1][i][j-1]+1);
ff[now%2][i][j]=max(ff[now%2][i][j],ff[(now%2)^1][i][j-1]);
}
}
}
}
int ans=1e9;
for(int i=1;i<=min(k+1,n);++i){
for(int j=1;j<=min(k+1,m);++j){
if(ff[k%2][i][j]){
ans=min(k-f[k%2][i][j],ans);
}
}
}
printf("%d\n",ans);
return 0;
}
二、给定一个序列,把所有的区间和找出来,求第K大值。
容易发现这道题的答案具有单调性。我们可以二分答案。每次二分一个mid,如果小于等于mid的个数大于等于k,让r=mid,否则让l=mid;最后l或r就是答案。这道题今天想到了二分,但一直在考虑二分到的值会不会不能组成。但是二分是不需要考虑那么多的, 你只需要求小于等于自己的个数>=k的最小值就一定是答案。二分一般做的就是这个东西,不用考虑那么多。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
char ch=getchar();ll num=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){num=(num<<1)+(num<<3)+ch-'0';ch=getchar();}
return num*f;
}
const int N=1e6+10;
int n,k;
ll a[N],l,r;
bool check(ll mid){
ll num=0;
int head=1;if(a[1]<=mid) ++num;
for(int i=2;i<=n;++i){
while(head<=i&&a[i]-a[head-1]>mid) head++;
num+=i-head+1;
}
if(num>=k) return 1;
return 0;
}
int main(){
// freopen("movie.in","r",stdin);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i) a[i]=read(),a[i]+=a[i-1];
l=1,r=a[n];
while(l+1<r){
ll mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid;
}
if(check(l)) printf("%lld\n",l);
else printf("%lld\n",r);
return 0;
}
三、给你一棵树,边权都是1,有q个询问,每次给一个集合s,让你对集合中的点两两配对,如果有一种方案满足路径和相加小于等于n,输出yes并且输出你的配对方案。否则输出no。
简单画一下图的话,大胆猜到没有no的情况。然后就是怎么配对了。我们先考虑一条链的话,你怎样配对最优,那就是不让有重边,然后没有重边的情况一定是按照dfs序从小到大一个一个配对。这个能不能推广到树上呢?
你发现,如果在树上你要配对的点的个数是偶数,那么就是按照dfs序配对就是最优的。不过如果有一个点最后无法配对,你需要找不属于这颗子树的其他点,那么这条链的边会被重新走一遍,会存在反例。这时候你考虑如果把配对的顺序向右推一位,它的边的使用情况会正好相错。那么如果你第一种方式长度大于n,第二种一定小于n。然后简单树上倍增求一下lca就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,s,num,tot,ver[N<<1],Next[N<<1],lin[N],dfn[N],f[N][30],dep[N];
bool v[N];
struct node{
int x,xu;
}e[N];
void add(int x,int y){
ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;
}
void dfs(int x,int deep,int fa){
v[x]=1;dep[x]=deep;
dfn[x]=++num;
for(int i=lin[x];i;i=Next[i]){
int y=ver[i];
if(v[y]) continue;
f[y][0]=x;
for(int j=1;j<=25;++j){
f[y][j]=f[f[y][j-1]][j-1];
}
dfs(y,deep+1,x);
}
}
bool cmp(node a,node b){
return a.xu<b.xu;
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=25;i>=0;i--)
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=25;i>=0;i--){
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
}
return f[x][0];
}
int main(){
freopen("kieru.in","r",stdin);
freopen("kieru.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;++i){
int x,y;scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs(1,1,0);
while(1){
int temp=0;
scanf("%d",&s);
if(s==0) break;
for(int i=1;i<=s;++i){
scanf("%d",&e[i].x);e[i].xu=dfn[e[i].x];
}
sort(e+1,e+s+1,cmp);
for(int i=1;i<=s;i+=2){
int x=e[i].x;int y=e[i+1].x;
int p=lca(x,y);
temp+=dep[x]+dep[y]-2*dep[p];
}
printf("Yes\n");
if(temp<=n){
for(int i=1;i<=s;i+=2){
printf("%d %d\n",e[i].x,e[i+1].x);
}
}else{
for(int i=2;i<=s;i+=2){
printf("%d %d\n",e[i].x,e[(i+1)%s].x);
}
}
}
return 0;
}