题目链接: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);
}
}