[bzoj4670][2-sat]佛罗里达

3 篇文章 0 订阅

Description

没有得到激光武器的苏联十分生气,他们决定派遣一支特种部队强行 登陆美国并造成一定的袭击。 Reddington 得到的情报是他们将在佛罗里达
海岸登陆,他决定派遣他的手下去阻击他们。可惜的是, Reddington 由于 不听从总统的意见,手中的部队只剩下了 N
个人。人与人之间会有一定的 矛盾值,第 i 个人与第 j 个人的矛盾值为 Ti,j,并且有 Ti,i = 0, Ti,j = Tj,i。
Reddington 希望将这 N 个人分为两支小分队,记为 A, B,每个人要么属 于分队 A 要么属于分队 B。对于一支小分队
S,其内部的不安值
在这里插入图片描述
显然的,假如一支分队的不安值很高,那么作战能力就会很差。现在 给定你 N 以及一个 N ∗ N 的矩阵 T,你需要告诉
Reddington,最小的 D(A) + D(B) 是多少。

Input

输入包括多组数据。 对于每组数据: 第一行一个整数 N。 接下来 N −1 行,第 i 行读入 N −i 个数,第 i 行第 j 个数表示
Ti,i+j。 2 ≤ N ≤ 250, 0 ≤ Ti,j ≤ 109,数据组数不超过 2 组

Output

一行,最小的 D(A) + D(B) 是多少

Sample Input

5

4 5 0 2

1 3 7

2 0

4

Sample Output

4

HINT

//一种最优方案为: A = {1, 2, 4}, B = {3, 5},此时 D(A) = 4, D(B) =0

题解

我居然开场就看错题了…挂机了半个多小时
明明就是要求max嘛qwq…
先考虑朴素做法
强行让 D ( A ) > D ( B ) D(A)>D(B) D(A)>D(B)
枚举 A A A集合中的最大边 c c c,我们可以二分 B B B集合的最大边 d d d
那么 c j > c c_j>c cj>c的,显然两个点要在他们之间
c &lt; = c j &lt; d c&lt;=c_j&lt;d c<=cj<d的,显然两个点不能在 B B B集合
剩余的边可以扔掉不管了
那么上面可以 2 − s a t 2-sat 2sat强行 n 4 l o g n^4log n4log
遇到这种情况肯定要找一些最优化的性质
对于一个偶环的最小边,他是不可能成为 A A A集合的最大边的,我们将他连接的两个点缩起来之后就出现了奇环,显然其不满足第一条限制
对于图中出现了奇环的,我们做完当前边后就退出。因为剩余的边显然均小于已加入的边,图中已出现奇环,不合法。但需要注意的是当前边是合法的
故在环上的操作仅有 1 1 1次,复杂度降为 n 3 l o g n^3log n3log
另外听说此题还有随机化的巧妙做法?

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=255;
struct edge{int x,y,c,next;}a[MAXN*MAXN*8],E[MAXN*MAXN*2];int len,last[2*MAXN];
bool cmp(edge n1,edge n2){return n1.c>n2.c;}
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}

int sta[2*MAXN],belong[2*MAXN],low[2*MAXN],dfn[2*MAXN],id,cnt,tp;
bool vis[2*MAXN];
void tarjan(int x)
{
	sta[++tp]=x;vis[x]=true;
	low[x]=dfn[x]=++id;
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(!dfn[y])tarjan(y),low[x]=min(low[x],low[y]);
		else if(vis[y])low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x])
	{
		cnt++;int i;
		do
		{
			i=sta[tp--];
			belong[i]=cnt;
			vis[i]=false;
		}while(i!=x);
	}
}

int rt[MAXN];
int findrt(int x){return rt[x]==x?rt[x]:rt[x]=findrt(rt[x]);}
int n,col[MAXN],vi[MAXN],tim;
vector<int> vec[MAXN];
void flu(int x)
{
	vi[x]=tim;
	for(int k=0;k<vec[x].size();k++)
	{
		int y=vec[x][k];
		if(vi[y]!=tim)col[y]=col[x]^1,flu(y);
	}
}
int ans,ln;
bool check(int u1,int u2)
{
	len=0;memset(last,0,sizeof(last));
	//1~n 选0   n+1~2*n 选1
	for(int i=1;i<=ln;i++)
	{
		if(E[i].c>u1)
		{
			ins(E[i].x,E[i].y+n);ins(E[i].x+n,E[i].y);
			ins(E[i].y,E[i].x+n);ins(E[i].y+n,E[i].x);
		}
		else if(E[i].c>u2)
		{
			ins(E[i].x+n,E[i].y);
			ins(E[i].y+n,E[i].x);
		}
		else break;
	}
//	puts("YES");
//	pr2(len);
	memset(low,0,sizeof(low));memset(dfn,0,sizeof(dfn));
	id=cnt=tp=0;memset(vis,false,sizeof(vis));
	for(int i=1;i<=2*n;i++)if(!dfn[i])tarjan(i);
	for(int i=1;i<=n;i++)if(belong[i]==belong[i+n])return false;
	return true;
}
int ct=0;
void work(int u)
{
//	ct++;
	int l=u,r=ln+1,sum;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(check(E[u].c,E[mid].c))sum=E[mid].c,l=mid+1;
		else r=mid-1;
	}
	ans=min(ans,E[u].c+sum);
}
int main()
{
//	freopen("1.in","r",stdin);
	while(scanf("%d",&n)!=EOF)
	{
		ln=0;
		for(int i=1;i<=n;i++)
			for(int j=i+1;j<=n;j++)E[++ln].c=read(),E[ln].x=i,E[ln].y=j;
		sort(E+1,E+1+ln,cmp);
		for(int i=1;i<=n;i++)rt[i]=i;
		memset(vi,0,sizeof(vi));tim=0;
		memset(col,-1,sizeof(col));
		ans=(1<<31-1);E[ln+1].c=0;
		for(int i=1;i<=ln;i++)
		{
			int x=E[i].x,y=E[i].y;
			int p=findrt(x),q=findrt(y);
			if(p!=q)
			{
				if(col[x]==-1)col[x]=0;
				vec[x].push_back(y);vec[y].push_back(x);tim++;
				flu(x);ct++;
				rt[p]=q;
				work(i);
			}
			else
			{
				if(col[x]==col[y]){work(i);break;}
				vec[x].push_back(y);vec[y].push_back(x);
			}
//			pr2(ct);
		}
		ct=0;
		pr2(ans);
		for(int i=1;i<=n;i++)vec[i].clear();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值