【NOIP2017提高A组模拟9.23】碎

题目描述

数据范围

时空限制

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 2sat,但我不会……

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+v2ans的时候,答案不会再比之前的优,可以直接结束

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值