把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P4643 [国家集训队]阿狸和桃子的游戏

题面传送门
首先,我们看到一个庞大的式子,好怕怕!
\(rsjdalao\)解读,这就是所选的点权加上两个点都是自己的点围成的边的边权。
那么不就好做了,贪心!能拿点权大的点就拿大的点!
代码实现:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int n,m,a[100039],x[100039],y[100039],z[100039],s[100039],ans,tot,so[100039];
inline bool cmp(int x,int y){
	return a[x]>a[y];
}
int main(){
	register int i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) scanf("%d",&a[i]),so[i]=i;
	for(i=1;i<=m;i++) scanf("%d%d%d",&x[i],&y[i],&z[i]);
	sort(so+1,so+n+1,cmp);
	for(i=1;i<=n;i++){
		s[so[i]]=i&1;
		if(i&1) ans+=a[so[i]];
		else tot+=a[so[i]];
	}
	for(i=1;i<=m;i++){
		if(s[x[i]]==s[y[i]]&&s[y[i]]==1) ans+=z[i];
		if(s[x[i]]==s[y[i]]&&s[y[i]]==2) tot+=z[i];
	}
	printf("%d",ans-tot);
}

很悲惨地爆\(zero\)了。为什么?因为他还有边权,这样的贪心不能保证边权最大。
那么,爆搜!能骗几分是几分。
代码实现:

#include<cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,a[100039],x[100039],y[100039],z[100039],s[100039],ans=2147483647,tot,pus,now;
inline void dfs(int h,int now){
	if(h>n) return;
	if(now==n/2) {
		tot=pus=0;
		for(register int i=1;i<=n;i++){
			if(s[i]) tot+=a[i];
			else pus+=a[i];
		}
		for(register int i=1;i<=m;i++){
			if(s[x[i]]==s[y[i]]&&s[y[i]]==1) tot+=a[i];
			if(s[x[i]]==s[y[i]]&&s[y[i]]==2) pus+=a[i];
		}
		ans=min(ans,tot-pus);
		return;
	}
	s[h]=1;
	dfs(h+1,now+1);
	s[h]=0;
	dfs(h+1,now);
} 
int main(){
	register int i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) scanf("%d",&a[i]);
	for(i=1;i<=m;i++) scanf("%d%d%d",&x[i],&y[i],&z[i]);
	dfs(1,0);
	printf("%d",ans);
}

万紫千红吧,一分都没拿到,也不知道是\(dfs\)打挂了还是本来就不能用\(dfs\)
重新关注一下题面: 为桃子的得分减去阿狸的得分。
题目为什么要这样出?不排除闲的无聊的可能性,但这可是国家集训队的题目!
想象一下,对于两个点,分类讨论一下。
设这两个点为\(x1\)\(x2\),点权为\(y1\)\(y2\),边权为\(z\)
若都是一个人拿去了,那么得分为\(y1+y2+z\)
若是不同人拿去了,那么分别得\(y1\)\(y2\)分。
把不同人拿去的情况转换成不等式,得\(y1<y2\).
根据不等式的性质,两边同时加上\(\frac{z}{2}\),得\(y1+\frac{z}{2}<y2+\frac{z}{2}\)
将同一人拿去的情况转变一下,得\((y1+\frac{z}{2})+(y2+\frac{z}{2})\)
两式都不改变得分差值情况,所以我们可以把两个点围成一条边的边权平均分配到两个点上。
记得开\(double\)
代码实现:

#include<cstdio>
#include<algorithm>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,x,y,z;
double a[100039],ans,tot;
inline void read(int &x){
	char s=getchar();int f=1;x=0;
	while(s<'0'||s>'9'){if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
	x*=f;
}
int main(){
	register int i,j;
	read(n);read(m);
	for(i=1;i<=n;i++) scanf("%lf",&a[i]);
	for(i=1;i<=m;i++) read(x),read(y),read(z),a[x]+=z/2.0,a[y]+=z/2.0;
	sort(a+1,a+n+1);
	for(i=1;i<=n;i++) {
		if(i&1) ans+=a[i];
		else tot+=a[i];
	}
	printf("%.0lf",tot-ans);
}

思维题呀!思维题。

posted @ 2020-03-14 21:27  275307894a  阅读(51)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end