11.8

    一、给定一个操作序列,由四种字符组成,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;
}

猜你喜欢

转载自blog.csdn.net/qq_39759315/article/details/83864233