题目描述
数据范围
时空限制
T i m e L i m i t s : 1000 m s M e m o r y L i m i t s : 131072 K B Time\ Limits:1000ms\\ Memory\ Limits: 131072KB Time Limits:1000msMemory Limits:131072KB
题目大意
有 n n n个点,两两之间都有一个陌生值,有两个集合,现在要把这些点放入这两个集合中,每个集合的陌生值定义为在这个集合内两点间最大的陌生值,求两个集合的最小陌生值之和
题目分析
这题正解本来是 2 − s a t 2-sat 2−sat,但我不会……
10 % 10\% 10%
直接暴力枚举每个点放在哪个集合,统计一下答案
40 % 40\% 40%
在暴力的基础上加优化,设两个集合的值分别是 v 1 v1 v1和 v 2 v2 v2,当 v 1 + v 2 ≥ a n s v1+v2\ge ans v1+v2≥ans的时候,答案不会再比之前的优,可以直接结束
100 % 100\% 100%
思考一下怎么让优化多执行。发现如果先加陌生值大的就更容易达到剪枝的条件,就可以更加节省时间。那么可以对于边按照陌生值从大到小排序,然后重构枚举点的顺序
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#define inf 2147483647
using namespace std;
struct node
{
int x,y,val;
};
int n,v1,v2,ans,tot;
int a[305][305],l[305],r[305],q[305];
bool b[305];
node map[90005];
int read()
{
int res=0,fh=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') fh=-1;ch=getchar();}
while (ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch-'0'),ch=getchar();
return res*fh;
}
bool cmp(node x,node y) {return x.val>y.val;}
void dg(int now)
{
if (v1+v2>=ans) return;//剪枝
int x=0,v=0;
if (now>n)
{
ans=v1+v2;
return;
}
for (int i=1;i<=l[0];++i)
v=max(v,a[l[i]][q[now]]);
l[++l[0]]=q[now];
x=v1;
v1=max(v,v1);
dg(now+1);
v1=x;
l[l[0]--]=0;
v=0;
for (int i=1;i<=r[0];++i)
v=max(v,a[r[i]][q[now]]);
r[++r[0]]=q[now];
x=v2;
v2=max(v,v2);
dg(now+1);
v2=x;
r[r[0]--]=0;
}
int main()
{
freopen("broken.in","r",stdin);
freopen("broken.out","w",stdout);
n=read();
for (int i=1;i<n;++i)
for (int j=1;j<=n-i;++j)
map[++tot].val=a[i+j][i]=a[i][i+j]=read(),map[tot].x=i,map[tot].y=i+j;
sort(map+1,map+tot+1,cmp);//按照陌生值大小排序
for (int i=1;i<=tot;++i)
{
if (!b[map[i].x]) q[++q[0]]=map[i].x;//重新构造顺序
if (!b[map[i].y]) q[++q[0]]=map[i].y;
b[map[i].x]=b[map[i].y]=true;
}
ans=inf;
dg(1);//暴力
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}