上升点列

题目来源

CSP2022-J-T4

题解0

根据数据规模k=0的情况有40%,可尝试使用BFS,结果只有10分,其他点提示TLE

//根据数据范围可知k=0数据为40%,数据规模不大可用BFS实现 
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const int max_xy=1e9+10;//xy的范围 
int n, k, x, y;
bool a[10000][10000];//用于标记是否存在该点,得分多少受制于该2维数组大小 
bool vis[10000][10000];//BFS过程中用于标记是否访问过 
struct node{
	int x, y;
	int s;//记录层数 
}p[510];
int ans;
int main()
{
	//freopen("point5.in", "r", stdin);
	cin>>n>>k;
	for(int i=0; i<n; i++){
		cin>>p[i].x>>p[i].y;
		a[p[i].x][p[i].y]=1;
	}
	
	for(int i=0; i<n; i++){
		memset(vis, 0, sizeof(vis));
		queue <node> que;
		p[i].s=1;  
		que.push(p[i]); 
		vis[p[i].x][p[i].y]=1;
		while(!que.empty()){
			node f, t;
			f=que.front();
			ans=max(ans, f.s);
			if(a[f.x+1][f.y] && vis[f.x+1][f.y]==0){//往右走 
				t.x=f.x+1; t.y=f.y; t.s=f.s+1;
				vis[f.x+1][f.y]=1; 
				que.push(t);
			}
			if(a[f.x][f.y+1] && vis[f.x][f.y+1]==0){//往左走 
				t.x=f.x; t.y=f.y+1; t.s=f.s+1;
				vis[f.x][f.y+1]=1;
				que.push(t);
			}
			que.pop();
		}
	}
	cout<<ans;
	return 0;
}

题解1,预计得分10分

数据分析,测试点1-2的n≤10,k=0。可采用傻傻的暴力得分
先取点后排序再判断是否符合题意
取点方法有两种:dfs和二进制,见链接
时间复杂度为指数级

#include<bits/stdc++.h>
using namespace std;
int n, k, ans=1;
struct node{
	int x, y;
}; 
node p[510], t[510];//p用于输入,t用于存放取出的点 
bool cmp(node a, node b){
	return a.x+a.y < b.x+b.y;
}
bool pd(int len){
	bool f=true;
	for(int i=1; i<len; i++)
		if(t[i].x+t[i].y-t[i-1].x-t[i-1].y != 1){
			f=false;break;
		}
	return f;
}
bool cni(int x, int y){//x个数取y个数,此处也可用dfs暴力选取
	bool f=false;
	for(int i=(1<<y)-1; i<(1<<x); i++){
		int num=0; int k=i;//num用于计数k的二进制位1的个数
		while(k){
			k=k&(k-1);
			num++;
		} 
		if(num==y){
			int cnt=0;
			memset(t, 0, sizeof(t));
			for(int j=0; j<x; j++)
				if(i&(1<<j))
					t[cnt++]=p[j];
			sort(t, t+cnt, cmp);
//			for(int w=0; w<cnt; w++)
//				cout<<t[w].x<<","<<t[w].y<<" ";
//			cout<<endl;
			if(pd(cnt))
				return true;
		} 
	}
	return f;
}
int main()
{
	cin>>n>>k;
	for(int i=0; i<n; i++)cin>>p[i].x>>p[i].y;
	for(int i=n; i>1; i--)
		if(cni(n, i))//从n个数中取出i个点 
		{
			ans=i;
			break;
		}
	cout<<ans;
	return 0;
}
/*
5 4
5 4
1 2
3 3
1 3
2 3
*/

题解2

先排序,以i点结束的最长上升子序列
根据题意可得,k=0时,与最长上升子序列非常相似,可获得40%的分数
实际可得30分

#include<bits/stdc++.h>
using namespace std;
int n, k, ans;
struct node{
	int x, y;
}; 
node p[510];//p用于输入
bool cmp(node a, node b){
	return a.x+a.y < b.x+b.y;
}
int dp[510];//以i点结束的最长上升子序列 
int main()
{
	cin>>n>>k;
	for(int i=1; i<=n; i++)cin>>p[i].x>>p[i].y;
	sort(p+1, p+1+n, cmp);
	for(int i=1; i<=n; i++){
		dp[i]=1;
		for(int j=1; j<i; j++){
			if(p[i].x+p[i].y-p[j].x-p[j].y == 1)
				dp[i]=max(dp[i], dp[j]+1);
		}
	}
	for(int i=1; i<=n; i++)
		ans=max(ans, dp[i]);
	cout<<ans;
	return 0;
}

题解3

根据题解2增加维度进行DP可得正解
int dp[i][k];//以i点结束已用k个点的的最长上升子序

#include<bits/stdc++.h>
using namespace std;
int n, k, ans;
struct node{
	int x, y;
}; 
node p[510];//p用于输入
bool cmp(node a, node b){
	return a.x+a.y < b.x+b.y;
}
int dp[510][510];//以i点结束已用k个点的的最长上升子序 
int main()
{
	cin>>n>>k;
	for(int i=1; i<=n; i++)cin>>p[i].x>>p[i].y;
	sort(p+1, p+1+n, cmp);
	for(int i=1; i<=n; i++){
		for(int j=0; j<=k; j++){
			dp[i][j]=1;
			for(int t=1; t<i; t++){
				if(p[t].x>p[i].x || p[t].y>p[i].y)continue;
				int l=p[i].x-p[t].x+p[i].y-p[t].y-1;//i点到t点需要添加的点数量 
				if(l>j)continue;
				dp[i][j]=max(dp[i][j], dp[t][j-l]+l+1); 
			}
			ans=max(ans, dp[i][j]+k-j);//还有一中情况在连续点上加k个点 
		}
	}
	cout<<ans;
	return 0;
}

posted @ 2022-11-30 18:20  TFLSNOI  阅读(245)  评论(0编辑  收藏  举报