E. Best Subsequence

  • “最大权值闭合图,即给定一张有向图,每个点都有一个权值(可以为正或负或0),你需要选择一个权值和最大的子图,使得子图中每个点的后继都在子图中。”
  • 这样的定义可以让你理解算法执行的逻辑,却难以在你赛场上遇到它时牵动你的思绪
  • 更符合你做题时真切感受的描述应该是:给你一些点,消耗一些点的代价可以得到另一些点的收益,要求收益最大化。 设想情境
  • 前置思考:最小割模型中两个集合的性质分别由源点和汇点刻画;求出最小割后,边权再无意义,我们只关心图的连通情况
  • 建模方法是:提取模型,点的取否类似于最小割中的点集划分,A->B类似于最小割中的“防止割断”;点边转化,把点的权值体现在边上;正负分类,源点连接收益点,汇点连接代价点(考虑到边权非负);源点为超级置位点,汇点为超级重置点,这样,割断s->x意味着不选某个收益点;割断y->t意味着选择某个代价点,两者均使得总收益减少,故要求最小割,初始所有的边都未被割断,对于s,意味着选择所有收益点;对于t,意味着不选所有代价点,得到初始收益
点击查看代码
#include <bits/stdc++.h>
using namespace std;
long long num[105];
int pr1[205],pr2[205],l[205];
int s,t,flow;
vector<int>a[205],c[205],d[205];
void add(int u,int v,int w)
{
	a[u].push_back(v);
	a[v].push_back(u);
	c[u].push_back(w);
	c[v].push_back(0);
	d[u].push_back(a[v].size()-1);
	d[v].push_back(a[u].size()-1);
}
void update()
{
	int cur=t;
	flow+=l[t];
	while(cur!=s)
	{
		c[pr1[cur]][pr2[cur]]-=l[t];
		c[cur][d[pr1[cur]][pr2[cur]]]+=l[t];
		cur=pr1[cur];
	}
}
long long v[205];
int q[3000005];
bool bfs()
{
	memset(v,false,sizeof(v));
	q[1]=s;
	l[s]=INT_MAX;
	int L=0,r=1;
	while(L<r)
	{
		L++;
		int n1=q[L];
		for(int i=0;i<a[n1].size();i++)
		{
			if(v[a[n1][i]]==false&&c[n1][i]>0)
			{
				r++;
				q[r]=a[n1][i];
				v[a[n1][i]]=true;
				l[a[n1][i]]=min(l[n1],c[n1][i]);
				pr1[a[n1][i]]=n1;
				pr2[a[n1][i]]=i;
				if(a[n1][i]==t)
				{
					return true;
				}
			}
		}
	}
	return false;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		cin>>n;
		for(int i=0;i<=200;i++)
		{
			a[i].clear();
			c[i].clear();
			d[i].clear();
		}
		s=199,t=200;
		for(int i=1;i<=n;i++)
		{
			cin>>num[i];
			add(s,i+60,1);
			for(int j=0;j<60;j++)
			{
				if((num[i]>>j)&1)
				{
					add(i+60,j,n);
				}
			}
		}
		for(int i=0;i<60;i++)
		{
			add(i,t,1);
		}
		flow=0;
		while(bfs())
		{
			update();
		}
		cout<<n-flow<<endl;
	}
	return 0;
}
posted @ 2024-10-29 22:49  D06  阅读(9)  评论(0编辑  收藏  举报