其他分享
首页 > 其他分享> > 10.18 考试

10.18 考试

作者:互联网

填坑ing

T1 bzoj 4057

Link

Desprition

有 \(n\) 个人之间互相欠钱,\(A\) 欠 \(B\) 钱记为 \(d[a][b]\), 保证 \(d[a][b]=-d[b][a]\) 。如果一个人入不 敷出(收入小于欠债)则会破产并消除所有与他有关的债务。破产不是一瞬间完成的,只有第 一个人破产后,接下来可能破产的人才会再继续破产,直到稳定。不同的结局将取决于谁先 破产。对于每个人,是否存在一种结局使得该人是唯一的幸者。

input

第一行一个正整数 \(T\) ,表示有 \(T\) 组数据。 每组数据第一行一个正整数 \(n\) ,接下来 \(n\) 行,每行 \(n\) 个整数,第 \(i\) 行第 \(j\) 个整数表示 \(d[i][j]\), 保证有\(d[i][i] = 0, d[i][j] = -d[j][i],\) \(|d[i][j]| <=10^6\)。

output

每组数据输出一行,按照升序输出所有可能的人编号,空格隔开,如果没有一个人能满足条 件,输出一个 0

对于 100% 的数据 \(n\leq 20\)

sloution

\(n\) 的范围很小,考虑状压 \(dp\)

设 \(f[i]\) 表示当前 \(i\) 这个集合里面的人全部破产能否达成 \(0表示没破产,1表示破产\)。

转移的时候枚举每个没有破产的人 \(j\),若这个人在 \(i\) 这个状态下可以破产,则 $f[i $ \(or\) \((1<<(j-1))]\) \(|= f[i]\)

复杂度为 \(O(2^n n)\) 。理论上来说是跑不过去的。实际上加个优化就过了。(我就这样被 \(T\) 成了 30分)

一个优化,如果 \(f[i] == 0\) 我们可以直接跳过,不用再枚举他。

最后看 \(f[(1<<n)-1 - (1<<(i-1))]\) 是否为 \(1\) ,若为 \(1\) 则 \(i\) 这个人可能成为最后的赢家。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = (1<<20);
int T,n,flag,a[21][21],sum[21],f[N+10];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
int main()
{
	T = read();
	while(T--)
	{
		n = read();
		memset(f,0,sizeof(f));
		memset(sum,0,sizeof(sum));
		for(int i = 1; i <= n; i++)
		{
			for(int j = 1; j <= n; j++)
			{
				a[i][j] = -1 * read();
				sum[i] += a[i][j];
			}
		}
		for(int i = 1; i <= n; i++)
		{
			if(sum[i] < 0) f[1<<(i-1)] = 1;
		}
		for(int i = 1; i < (1<<n)-1; i++)
		{
			if(f[i])
			{
				for(int j = 1; j <= n; j++)
				{
					if(((i>>(j-1)) & 1) == 0)
					{
						int num = 0;
						for(int k = 1; k <= n; k++)
						{
							if(((i>>(k-1))&1) == 1)
							{
								num += a[j][k];
							}
						}
						if(sum[j] - num < 0) f[i|(1<<(j-1))] |= f[i];
					}
				}
			}
		}
		int M = (1<<n)-1, flag = 0;
		for(int i = 1; i <= n; i++)
		{		
			if(f[M-(1<<(i-1))] == 1)
			{
				flag = 1;
				printf("%d ",i);
			}
		}
		if(flag == 0) printf("%d",0);
		printf("\n");
	}
	return 0;
}

T2 bzoj 5450

Desprition

\(n\) 个点,\(m\) 条边的有向图,每次选择若干点将其标记,不能存在两个不同的点 \(i,j\) 满足 可以从 \(i\) 到达 \(j\) 。问最少取几次可以使所有点都被标记过。

对于 100% 的数据 \(n\leq 10^6, m\leq 10^6\)

Sloution

显然在一个环中,我们要取完这个环至少需要 环上点的数量的步数。

这个可以直接拿 \(tarjain\) 解决。然后就转化成我们熟悉的 \(DAG\) 上的问题。

显然答案为 最长路的长度。直接拓扑排序就行。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int N = 1e6+10;
int n,m,cnt,tot,num,top,ans,u,v;
int head[N],du[N],dfn[N],low[N],sta[N],f[N],shu[N],siz[N];
bool vis[N];
vector<int> e1[N];
struct node
{
	int to,net;
}e[N];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
void add(int x,int y)
{
	e[++tot].to = y;
	e[tot].net = head[x];
	head[x] = tot;
}
void tarjain(int x)
{
	dfn[x] = low[x] = ++num;
	sta[++top] = x; vis[x] = 1;
	for(int i = head[x]; i; i = e[i].net)
	{
		int to = e[i].to;
		if(!dfn[to])
		{
			tarjain(to);
			low[x] = min(low[x],low[to]);
		}
		else if(vis[to])
		{
			low[x] = min(low[x],dfn[to]);
		}
	}
	if(dfn[x] == low[x])
	{
		cnt++; int y;
		do
		{
			y = sta[top--];
			shu[y] = cnt;
			siz[cnt]++;
			vis[y] = 0;
		}while(x != y);
	}
}
void rebuild()
{
	for(int i = 1; i <= n; i++)
	{
		for(int j = head[i]; j; j = e[j].net)
		{
			int to = e[j].to;
			if(shu[i] != shu[to])
			{
				du[shu[to]]++;
				e1[shu[i]].push_back(shu[to]);
			}
		}
	}
}
void topu()
{
	queue<int> q;
	for(int i = 1; i <= cnt; i++)
	{
		if(du[i] == 0)
		{
			q.push(i);
			f[i] = siz[i];
		}
	}
	while(!q.empty())
	{
		int t = q.front(); q.pop();
		for(int i = 0; i < e1[t].size(); i++)
		{
			int to = e1[t][i];
			f[to] = max(f[to],f[t] + siz[to]);
			if(--du[to] == 0) q.push(to);
		}
	}
	for(int i = 1; i <= cnt; i++) ans = max(ans,f[i]);
}
int main()
{
	n = read(); m = read();
	for(int i = 1; i <= m; i++)
	{
		u = read(); v = read();
		add(u,v);
	}
	for(int i = 1; i <= n; i++)
	{
		if(!dfn[i]) tarjain(i);
	}
	rebuild();
	topu();
	printf("%d\n",ans);
	return 0;
}

T3 bzoj 4552

原题

标签:ch,leq,int,10.18,num,破产,include,考试
来源: https://www.cnblogs.com/genshy/p/13882420.html