【题解】可爱路径【第四周】

update 8/22:本题已经联通了,所以不用跑连通块,不过影响不大。dp部分可以用拓扑排序预处理,不跑dfs,这样更快。

题目描述

小周猪猪手里拿到一个地图,地图显示的是一个n个点和m条边的连通的有向无环图。

现在小周猪猪需要寻找一条路径,使得这条路径是可爱路径且可爱路径的可爱度最大。

一条路径是可爱路径当且仅当可以从路径的一端走到路径的另一端,且路径经过的边数一定要大于或等于k。且路径上的每一个节点只能够经过一次。

在这里,可爱值定义为:一串n个在可爱路径上的点的点权形成一个升序序列后第int(n/2.0) + 1个数。

现在,小周猪猪想知道可爱路径的最大可爱值,请你输出这个最大可爱值。

如果地图中不存在可爱路径输出,则输出No.

输入格式

第一行:三个数,分别是n, m和 k 。表示点的个数,边的条数以及可爱路径经过边数的约束条件。

第二行:共有n个数,第i个数表示节点i的点权。

接下来m行:每行两个数x和y,表示x到y有一条有向边。

输出格式

所有可爱路径中的最大可爱值。

样例输入1:

7 8 3
46 79 97 33 22 1 122
1 2
1 5
2 3
2 6
3 4
6 4
5 7
4 7

扫描二维码关注公众号,回复: 11839429 查看本文章

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

分析:

首先拿到这道题,是毫无思路的。对于中位数,我们可以这样理解:设路径的长度为k,可以二分中位数x,看是否有(k+1)/2个数大于x。于是首先二分中位数。

对于每个点,若>=x,则点权为1;否则点权为-1。判断是否有一条>=k的路径,其点权的和>=0

本题可以暴搜,但会超时。其复杂度极高。于是只能用DP优化。

设dp[i][j]表示到达i点,且路径长度为j,此时的最大点权。该状态显然满足最优子结构,而且由于是DAG,所以无后效性。

我们再用dfs传入三个参数x,y,z,表示点,边数和当前权值。当z<=dp[x][y]时,直接retuen;否则更新dp[x][y],若z>=0&&y>=k,则return 1,否则继续搜索,直到回溯为止。

由于不知道起点,可以一重循环枚举。时间复杂度大概是 O ( n 2 l o g ( 2 ∗ 1 e 9 ) ) O(n^2log(2*1e9)) On2log(21e9)),但是枚举起点会有迭代,dfs也有判断有解时的剪枝。可以卡过大部分数据,有70pts。

对于剩下的n<=1e5,这个n连二维数组都开不起啊!好在只是吓人的,他告诉你每条边都是连接相邻边,其实就是单链。跑一个连通块,用前缀和判断是否有长度>=k的两个端点,且在同一连通块内,差>0。时间复杂度 O ( n l o g ( 2 ∗ 1 e 9 ) ) O(nlog(2*1e9)) Onlog(21e9))

这个问题就用朴素的方法解决了。注意开O2,否则过不了。

//dfs
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
const int M=1e5+5;



void read(int &x) {
    
    
	int f=1;x=0;char c=getchar();
	while(c<'0'||c>'9') {
    
    if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {
    
    x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	x*=f;
}


int cnt,n,m,k,dp[N][N],g[M],gg[M],Dp[M],sum[M],con[M],maxn[M];
bool flag=1;
vector<int> son[M];

void search(int x) {
    
    
	con[x]=cnt;
	for(int i=0;i<son[x].size();i++) {
    
    
		int y=son[x][i];
		if(!con[y]) search(y);
	}
}


bool dfs(int x,int y,int z) {
    
    
	if(z<=dp[x][y]) return 0;
	dp[x][y]=z;
	if(z>=0&&y>=k) return 1;
	for(int i=0;i<son[x].size();i++) {
    
    
		int u=son[x][i];
		if(dfs(u,y+1,z+gg[u])) return 1;
	}
	return 0;
}

bool check(int mid) {
    
    
	for(int i=1;i<=n;i++) {
    
    
		if(g[i]>=mid) gg[i]=1;
		else gg[i]=-1;
	}
	for(int j=1;j<=n;j++) {
    
    
		for(int k=0;k<=n;k++) {
    
    
			dp[j][k]=-0x3f3f3f3f;
		}
	}
	for(int i=1;i<=n;i++) {
    
    
		if(dfs(i,0,gg[i])) return 1;
	}
	return 0;
}


bool check2(int mid) {
    
    
	int tot=-0x3f3f3f3f;
	for(int i=1;i<=n;i++) {
    
    
		if(g[i]>=mid) gg[i]=1;
		else gg[i]=-1;
		sum[i]=sum[i-1]+gg[i];
		if(con[i]==con[i-1]) {
    
    
			maxn[i]=min(maxn[i-1],sum[i]);
		}
		else maxn[i]=sum[i];
	}
	for(int i=1;i<=n;i++) {
    
    
		int j=i-k;
		if(j>=1&&con[i]==con[j]) tot=max(tot,sum[i]-maxn[j]);
		if(tot>=0) return 1;
	}
	return 0;
}

int main() {
    
    
	read(n),read(m),read(k);
	for(int i=1;i<=n;i++) read(g[i]);
	for(int i=1;i<=m;i++) {
    
    
		int x,y;
		read(x),read(y);
		son[x].push_back(y);
		if(x-1!=y) flag=0;
	}
	if(flag) {
    
    
		for(int i=n;i>=1;i--) {
    
    
			if(!con[i]) {
    
    
				cnt++;
				search(i);
			}
		}
		int l=-1e9,r=1e9,ans=0;
		while(l<=r) {
    
    
			int mid=(1LL*(l+r))>>1;
			if(check2(mid)) l=mid+1,ans=mid;
			else r=mid-1;
		}
		if(ans==0) printf("No");
	    else printf("%d",ans);
	}
	else {
    
    
		int l=-1e9,r=1e9,ans=0;
		while(l<=r) {
    
    
			int mid=(1LL*(l+r))>>1;
			if(check(mid)) l=mid+1,ans=mid;
			else r=mid-1;
		}
		if(ans==0) printf("No");
		else printf("%d",ans);	
	}

}
//topo
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
const int M=1e5+5;



void read(int &x) {
    
    
	int f=1;x=0;char c=getchar();
	while(c<'0'||c>'9') {
    
    if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {
    
    x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	x*=f;
}


int cnt,n,m,k,dp[N][N],g[M],gg[M],Dp[M],sum[M],maxn[M];
int top,topo[N],in[N];
bool flag=1;
vector<int> son[M];


void toposort() {
    
    
	queue<int> q;
	for(int i=1;i<=n;i++) if(!in[i]) q.push(i);
	while(q.size()) {
    
    
		int x=q.front();q.pop();
		topo[++top]=x;
		for(int i=0;i<son[x].size();i++) {
    
    
			int y=son[x][i];
			if(--in[y]==0) q.push(y);
		}
	}
}





bool check(int mid) {
    
    
	for(int i=1;i<=n;i++) {
    
    
		if(g[i]>=mid) gg[i]=1;
		else gg[i]=-1;
	}
	for(int j=1;j<=n;j++) {
    
    
		for(int k=0;k<=n;k++) {
    
    
			dp[j][k]=-0x3f3f3f3f;
		}
	}
	for(int i=1;i<=n;i++) {
    
    
		int x=topo[i];
		dp[x][0]=gg[x];
		for(int j=0;j<=n;j++) {
    
    
			if(dp[x][j]>=0&&j>=k) return 1;
			for(int k=0;k<son[x].size();k++) {
    
    
				int y=son[x][k];
				dp[y][j+1]=max(dp[y][j+1],dp[x][j]+gg[y]);
			}
		}
	}
	return 0;
}


bool check2(int mid) {
    
    
	maxn[0]=0x3f3f3f3f;
	int tot=-0x3f3f3f3f;
	for(int i=1;i<=n;i++) {
    
    
		if(g[i]>=mid) gg[i]=1;
		else gg[i]=-1;
		sum[i]=sum[i-1]+gg[i];
		maxn[i]=min(maxn[i-1],sum[i]);
	}
	for(int i=1;i<=n;i++) {
    
    
		int j=i-k;
		tot=max(tot,sum[i]-maxn[j]);
		if(tot>=0) return 1;
	}
	return 0;
}

int main() {
    
    
	read(n),read(m),read(k);
	for(int i=1;i<=n;i++) read(g[i]);
	for(int i=1;i<=m;i++) {
    
    
		int x,y;
		read(x),read(y);
		son[x].push_back(y);
		in[y]++;
		if(x-1!=y) flag=0;
	}
	if(flag) {
    
    
		int l=-1e9,r=1e9,ans=0;
		while(l<=r) {
    
    
			int mid=(1LL*(l+r))>>1;
			if(check2(mid)) l=mid+1,ans=mid;
			else r=mid-1;
		}
		if(ans==0) printf("No");
	    else printf("%d",ans);
	}
	else {
    
    
		toposort();
		//for(int i=1;i<=top;i++) printf("%d ",topo[i]);
		int l=-1e9,r=1e9,ans=0;
		while(l<=r) {
    
    
			int mid=(1LL*(l+r))>>1;
			if(check(mid)) l=mid+1,ans=mid;
			else r=mid-1;
		}
		if(ans==0) printf("No");
		else printf("%d",ans);	
	}

}

猜你喜欢

转载自blog.csdn.net/cqbzlydd/article/details/108112332