【CF】CF1430_G Yet Another DAG Problem_最小割/网络流/状压dp

链接

codeforces 1430G

题面

题解

状压dp

这个方法官方题解里面写得还挺好的。

我就不写全了。仅仅简单地记录一下而已。

  • 我们考虑最终这幅图相当于说你给他分个层,使得每条边都从深度浅的连向深度深的。
  • 首先我们能用一些方法把边权变成点权。这样事情就由深度差乘边权变成了深度乘点权。好处理多了。
  • 分层了之后,自然就有一个结论:层数不超过点数。
  • 分层了,那我们就用状压dp,记(dp_{i,j})表示当前分完了第i层,已经分好了的集合为j。
  • 现在我们可以枚举子集转移了,复杂度(O(n3^n))
  • 我们希望能枚举点转移。但是硬转移肯定是不行的,因为我们有可能连出在同一层的边。那我们同一层的点拓扑排序完了之后倒叙添加就不会有这种事情了。

最小割

  • 我们同样边权变点权。记点权为(a_i)
  • 我们对于每个点分出一组n个点,编号从1到n,分别表示这个点选择这个深度,并由第i组点的第j个点向该组第j+1个点连一条((j+1)a_i)的边。这样我们就可以计算出代价了,接下来我们要加限制。
  • 限制,如果有一条边(x,y),那么对于每个深度的x的点向最浅的一直到下个深度的y的点连一条INF边。
  • 注意到可能会有负权边。考虑到每一组的计算代价的边会且只会割一条。那我们给这些边统一加一个超级大数就行了。具体来说,这个超级大数可以参考最小的负权边来定。
  • 最后最小割,用一个bfs走还能走的边给点染色。然后看看那些从被染色的点到没被染色的点的边就是被割掉边。

代码

状压dp

#include<bits/stdc++.h>
#define LL long long
#define MAXN 18
#define INF 1000000000000
using namespace std;
template<typename T> void Read(T &cn)
{
	char c; int sig = 1;
	while(!isdigit(c = getchar())) if(c == '-') sig = 0;
	if(sig) {cn = c-48; while(isdigit(c = getchar())) cn = cn*10+c-48; }
	else    {cn = 48-c; while(isdigit(c = getchar())) cn = cn*10+48-c; }
}
template<typename T> void Write(T cn)
{
	int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
	if(cn < 0 || cx < 0) {putchar('-'); cn = 0-cn; cx = 0-cx; }
	while(cn)cm = cm*10+cn%10,cn/=10,wei++;
	while(wei--)putchar(cm%10+48),cm/=10;
	putchar(cx+48);
}
template<typename T> void WriteL(T cn) {Write(cn); puts(""); }
template<typename T> void WriteS(T cn) {Write(cn); putchar(' '); }
template<typename T> void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; }
template<typename T> void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; }
int n,m;
struct Dp_lai{
	int a[MAXN+1];
	LL ans;
	void qing() {ans = INF; memset(a,0,sizeof(a)); }
	void outit() {for(int i = 1;i<=n;i++) WriteS(a[i]); puts(""); }
};
int e[MAXN+1][MAXN+1], g[MAXN+1];
LL de[MAXN+1];
Dp_lai f[1<<MAXN];
int du[MAXN+1], dui[MAXN+1];
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	Read(n); Read(m);
	for(int i = 1;i<=n;i++) for(int j = 1;j<=n;j++) e[i][j] = 0;
	memset(du,0,sizeof(du)); memset(de,0,sizeof(de));
	for(int i = 1;i<=m;i++) {int bx,by; LL bz; Read(bx); Read(by); Read(bz); e[by][bx] = 1; de[bx] += bz; de[by] -= bz; du[bx]++; }
	for(int i = 1;i<=n;i++)
	{
		g[i] = 0;
		for(int j = 1;j<=n;j++) if(e[j][i]) g[i] |= 1<<(j-1);
	}
	int l = 0, r = 0;
	for(int i = 1;i<=n;i++) if(!du[i]) dui[++r] = i;
	while(l < r)
	{
		int dang = dui[++l];
		for(int i = 1;i<=n;i++) if(e[dang][i]) 
		{
			du[i]--;
			if(!du[i]) dui[++r] = i;
		}
	}
	for(int i = 0;i<(1<<n);i++) f[i].qing(); 
	f[0].ans = 0; 
	for(int i = 1;i<=n;i++) for(int j = n;j>=1;j--) for(int k = 0;k<(1<<n);k++)
	{
		int bx = dui[j];
		if(((k&(1<<(bx-1)))!=0) || ((k&g[bx])!=g[bx]) || (f[k].ans+i*de[bx] >= f[k|(1<<(bx-1))].ans)) continue;
		f[k|(1<<(bx-1))] = f[k]; f[k|(1<<(bx-1))].ans += i*de[bx]; f[k|(1<<(bx-1))].a[bx] = i;
	}
	f[(1<<n)-1].outit();
	return 0;
}

最小割

#include<bits/stdc++.h>
#define LL long long
#define MAXN 20
#define INF 1000000000
using namespace std;
template<typename T> void Read(T &cn)
{
	char c; int sig = 1;
	while(!isdigit(c = getchar())) if(c == '-') sig = 0;
	if(sig) {cn = c-48; while(isdigit(c = getchar())) cn = cn*10+c-48; }
	else    {cn = 48-c; while(isdigit(c = getchar())) cn = cn*10+48-c; }
}
template<typename T> void Write(T cn)
{
	int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
	if(cn < 0 || cx < 0) {putchar('-'); cn = 0-cn; cx = 0-cx; }
	while(cn)cm = cm*10+cn%10,cn/=10,wei++;
	while(wei--)putchar(cm%10+48),cm/=10;
	putchar(cx+48);
}
template<typename T> void WriteL(T cn) {Write(cn); puts(""); }
template<typename T> void WriteS(T cn) {Write(cn); putchar(' '); }
template<typename T> void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; }
template<typename T> void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; }
namespace Flow{
	const int MAXB = MAXN*MAXN*MAXN*MAXN;
	const int MAXD = MAXN*MAXN+2;
	struct qwe{
		int a,b,ne,f;
		void mk(int ca, int cb, int cn, int cf) {a = ca; b = cb; ne = cn; f = cf; }
	};
	qwe a[MAXB*2+1];
	int alen;
	int head[MAXD+1], lst[MAXD+1];
	int shen[MAXD+1], dui[MAXD+1];
	int typ[MAXD+1];
	int bfs(int cn, int cm, int ctot)
	{
		for(int i = 1;i<=ctot;i++) shen[i] = ctot+2, lst[i] = head[i];
		int l = 0, r = 0; dui[++r] = cn; shen[cn] = 0;
		while(l < r)
		{
			int dang = dui[++l];
			for(int i = head[dang];i;i = a[i].ne)
			{
				int y = a[i].b;
				int lin = shen[dang]+1;
				if(!a[i].f || shen[y] <= lin) continue;
				shen[y] = lin; dui[++r] = y;
			}
		}
		return shen[cm] != ctot+2;
	}
	LL ansl;
	int dfs(int cn, int cm, int liu)
	{
		if(cn == cm) {ansl = ansl + liu; return liu; }
		for(int &i = lst[cn];i;i = a[i].ne)
		{
			int y = a[i].b;
			if(!a[i].f || shen[y] != shen[cn]+1) continue;
			int lin = dfs(y, cm, min(liu, a[i].f));
			if(lin) {
				a[i].f -= lin;
				a[((i+1)^1)-1].f += lin;
				return lin;
			}
		}
		return 0;
	}
	void get_typ(int cn, int ctot)
	{
		for(int i = 1;i<=ctot;i++) shen[i] = 0, typ[i] = 0;
		int l = 0, r = 0; shen[cn] = 1; typ[cn] = 1; dui[++r] = cn;
		while(l < r)
		{
			int dang = dui[++l];
			for(int i = head[dang];i;i = a[i].ne)
			{
				int y = a[i].b;
				if(!a[i].f) continue;
				typ[y] = 1; if(!shen[y]) shen[y] = 1, dui[++r] = y;
			}
		}
	}
	void flow(int cn, int cm, int ctot)
	{
		while(bfs(cn,cm,ctot)) while(dfs(cn,cm,INF)); 
		get_typ(cn, ctot); 
	}
	void build() {alen = 0; memset(head,0,sizeof(head)); }
	void lian(int cn, int cm, int cf) {a[++alen].mk(cn,cm,head[cn],cf); head[cn] = alen; }
	void lian_d(int cn, int cm, int cf) {lian(cn,cm,cf), lian(cm,cn,0); }
};
using Flow::build;
using Flow::lian_d;
using Flow::flow;
int n, m;
int e[MAXN+1][MAXN+1];
int de[MAXN+1];
int ans[MAXN+1];
int pos[MAXN+1][MAXN+1];
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	Read(n); Read(m);
	for(int i = 1;i<=n;i++) for(int j = 1;j<=n;j++) e[i][j] = 0;
	memset(de,0,sizeof(de));
	for(int i = 1;i<=m;i++) {int bx,by; LL bz; Read(bx); Read(by); Read(bz); e[by][bx] = 1; de[bx]+=bz; de[by]-=bz; }
	LL xiao = 0;
	for(int i = 1;i<=n;i++) Min(xiao, (LL)de[i]); xiao = xiao*n-1;
//	printf("xiao = %lld
",xiao);
	build();
	for(int i = 1;i<=n;i++) 
	{
		int lst = 1;
		for(int j = 1;j<=n;j++) 
		{
			int xian = 1+n*(i-1)+j;
			lian_d(lst, xian, de[i]*j-xiao);
			pos[i][j] = Flow::alen;
//			printf("  %d
",Flow::a[pos[i][j]].f);
			for(int k = 1;k<=n;k++) if(e[i][k]) lian_d(lst, 1+n*(k-1)+j, INF);
			lst = xian;
		}
		lian_d(lst, 1+n*n+1, INF);
	}
//	printf("pred
");
	flow(1, 1+n*n+1, 1+n*n+1);
//	printf("flow over
");
	for(int i = 1;i<=n;i++) 
	{
		int lst = 1;
		for(int j = 1;j<=n;j++)
		{
			int xian = 1+n*(i-1)+j;
			if(Flow::typ[lst] == 1 && Flow::typ[xian] == 0) ans[i] = j;
			lst = xian;
		}
	}
	for(int i = 1;i<=n;i++) WriteS(ans[i]); puts("");
	return 0;
}
原文地址:https://www.cnblogs.com/czyarl/p/13832655.html