CodeVS&Luogu 间谍网络

移步到新Blog查看此文章的更新




这个题大概思路就是一发裸的Tarjan然后建一个缩点的图。

读入之后先来一发Tarjan(注意有可能有多个连通图),记录下每个点属于的强连通分量。

然后缩点,建一个DAG,这时根据DAG的结构,肯定存在若干入度为0的点

下面就是重点了,只需要收买(也就是计算这个强连通分量中Minimize的Money)所有入度为0的缩点,也叫根点(自造的词)就一定可以抓完 全图的 间谍。

假如收买了根点之后无法收买缩点u,那么缩点u一定不是根点的子结点,也就意味着u绝对不和这个DAG联通,那u的入度为0(U肯定也是根点),就要收买u,所以收买所有根点是可以抓整个DAG的间谍的。

所以,当根点无法被收买的时候,那无解,统计一下无法被收买的强连通分量(也就是根点)之中最小的点,然后输出即可

或者,有解,那么就统计根点的最小花费,加起来就OK。

不需要DFS/BFS。统计的时候可以玩枚举(强连通分量不会太多……)


#include
#include
#include
#include
#include
using namespace std;
//建图
struct edge
{
	int pre,to;
} edge[8010]={0};
int pointer[3010]={0},fx,tx,edgenum/*边数*/,nodenum/*点数*/,canbuynum/*带权点数*/;
//Tarjan算法 
int dfn[3010]={0},low[3010]={0},tstamp=0/*时间戳*/,header=0/*栈指针*/,stack[10010]={0}/*手动栈*/;
bool init[3010]={false};
//记录强连通分量以及缩点图 
int numbers[3010]={0}/*点属于哪个分量*/,counter=0/*记录强连通分量个数*/,/*缩点图*/inw[3010]={0},prex2;
int moneyx[3010]/*点权记录*/,cache1,cache4;//上面两个缓存是读点权的 
//答案处理 
long long minimoney,ans=0;//每个分量最小花费和总答案 
bool flag=false,flag2=false;//flag2是判断有没有一个缩点是无法被收买的 flag记录枚举过程中的点 
int mininode=10000,mininode2=10000;//没法收买的最小的点(第一个是枚举过程中的缓存) 
//链式前向星建图 
void Insert(int at,int fromx,int tox)
{
	edge[at].pre=pointer[fromx];
	edge[at].to=tox;
	pointer[fromx]=at;
	return;
}
//Tarjan模板 
void tarjan(int u)
{
	int cache2,cache3,prex;
	dfn[u]=low[u]=++tstamp;
	stack[++header]=u; init[u]=true;
	prex=pointer[u];
	while (prex>0)
	{
		cache2=edge[prex].to;
		if (dfn[cache2]==0)
		{
			tarjan(cache2);
			low[u]=min(low[u],low[cache2]);
		}
		else if (init[cache2]==true) low[u]=min(low[u],dfn[cache2]);
		prex=edge[prex].pre;
	}
	if (low[u]==dfn[u])
	{
		counter++;
		do
		{
			cache3=stack[header];
			header--;
			numbers[cache3]=counter;
			init[cache3]=false;
		} while (cache3!=u);
	}
	return;
}

void Shrink()
{
	for (int i=1;i<=nodenum;i++)
	{
		prex2=pointer[i];
		while (prex2>0)
		{
			if (numbers[i]!=numbers[edge[prex2].to])//一条边连接的两个点如果不处于一个强连通分量之中 
			{
				inw[numbers[edge[prex2].to]]++;//增加入度和出度 
			}
			prex2=edge[prex2].pre;//链式前向星…… 
		}
	}
	return;
}

void getminimoney(int u)
{
	minimoney=100000; flag2=false; mininode=10000;//初始化 
	for (int j=1;j<=nodenum;j++)
	{
		//cout<<"点 "<


你可能感兴趣的:(CodeVS&Luogu 间谍网络)