百度AI小课堂-上升子序列(中等)(二分图染色+分组背包)

 百度AI小课堂-上升子序列(中等)

题目链接:https://nanti.jisuanke.com/t/39266

问答问题反馈

 

题目描述

给一个长度为 nn 的数组 aa 。试将其划分为两个严格上升子序列,并使其长度差最小。

 

对于每组数据输出一行一个整数,表示两个子序列的最小长度差。若不存在划分方案则输出 −1。

 

 

2
7
1 4 2 5 6 3 7
5
5 4 3 2 1

样例输出复制

1
-1

这个题和上次西安的D题做法类似。

不过这里建图的方法不是很明显,就是将i<j 且a[i]>=a[j]的连一条边。因为要递减。若这个图不是二分图,那么输出-1

然后染色处理每一个连通块分成两个部分。每个部分必须选一次,那就是赤裸裸的分组背包的题。

AC代码:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define in(x) scanf("%d",&x)
#define ind(x) scanf("%lld",&x)
#define out(x) printf("%d ",x);
#define outln(x) printf("%lld\n",x);
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
#define PE(x,y) x=((x)+(y))%mod
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
ll powmod(ll a,ll b) {ll res=1;a%=mod; 
for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
const int N=1e3+10;
int a[N],col[N];
int d[N][3],ma[N][N],n,dp[N][N];
bool flag;
void bfs(int root,int cnt){
	col[root]=1;
	queue<int>que;que.push(root);
	int s1=0,s2=0;
	while(que.size()){
		int u=que.front();que.pop();
		if(col[u]==1) s1++;
		else s2++;
		for(int i=1;i<=n;i++){
			if(ma[u][i]){
				if(col[i]==col[u]) {
					flag=0;return ;
				}
				if(col[i]!=-1) continue;
				col[i]^=col[u];
				que.push(i);
			}
		}
	}
	d[cnt][1]=s1;
	d[cnt][2]=s2;
}
void init(){
	memset(d,0,sizeof(d));
	mem(dp,0);
	mem(ma,0);
	mem(col,-1);
}
int main(){
	int _;in(_);
	while(_--){
		in(n);
		init();
		rep(i,1,n)	in(a[i]);
		rep(i,1,n){
			rep(j,i+1,n){
				if(i<j&&a[i]>=a[j]){
					ma[i][j]=ma[j][i]=1;
				}
			}
		}
		int cnt=0;
		flag=1;
		for(int i=1;i<=n&&flag;++i){
			if(col[i]==-1){
				bfs(i,++cnt);
			}
		}
		if(flag==0){
			printf("-1\n");
			continue;
		}
		dp[0][0]=1;
		rep(i,1,cnt){
			for(int p=n;p>=0;p--){
				rep(k,1,2){
					if(p-d[i][k]>=0){
						dp[i][p]|=dp[i-1][p-d[i][k]];
					}
				}
			}
		}
		int mi=0x3f3f3f3f,dnum;
		for(int p=0;p<=n;++p){
			if(dp[cnt][p]){
				int pp=n-p;
				int dnum=abs(pp-p);
				if(dnum<mi) mi=dnum;
			}
		}
		printf("%d\n",mi);
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ccsu_deer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值