1. 程式人生 > >【六省聯考2017】壽司餐廳

【六省聯考2017】壽司餐廳

題面

題解

有經驗的選手可以根據權值不重複計算的特性看出這是一個最大權閉合子圖問題;

我看了題解就知道這是一個最大權閉合子圖問題。。。

建邊:

  1. 對於所有的區間$[i,j]$的收益,將它們都看成點

    如果權值為正,從源點連來,容量為權值

    否則連向匯點,容量為權值的絕對值

  2. 將區間$[i,j]$,向區間內$i,j$連邊,容量為$\infty$,表示選對應的壽司就選對應的區間

  3. 對於所有壽司型別$a[i]$,將它們向匯點連邊,容量為$m\times a[i]^2$

  4. 對於每一個壽司,向它們所屬型別連邊,容量$\infty$,向匯點連邊,容量為$a[i]$

  5. 對於所有的$[i,j]$區間,向$[i + 1,j]$和$[i, j - 1]$,容量為$\infty$,表示選了大區間肯定選其中的小區間

$ans$就是總收益減去最小割就可以了

程式碼

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register
#define clear(x, y) memset(x, y, sizeof(x))

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while(ch != '-' && (!isdigit(ch))) ch = getchar();
	if(ch == '-') w = -1, ch = getchar();
	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

const int maxn(40010), maxm(110), INF(0x3f3f3f3f);
struct edge { int next, to, cap; } e[maxn << 6];
int head[maxn], cur[maxn], e_num = -1;
inline void add_edge(int from, int to, int cap)
{
	e[++e_num] = (edge) {head[from], to, cap}; head[from] = e_num;
	e[++e_num] = (edge) {head[to], from,  0 }; head[to]   = e_num;
}

bool bfs();
int dfs(int x, int f);
inline int Dinic();
int n, m, a[maxm], g[maxm][maxm];
int id[maxm][maxm], ida[maxn], idp[maxm], S, T;
long long ans;

void init_id()
{
	int idcnt = 0; S = ++idcnt;
	for(RG int i = 1; i <= n; i++)
		for(RG int j = i; j <= n; j++)
			id[i][j] = ++idcnt;
	for(RG int i = 1; i <= n; i++) if(!ida[a[i]]) ida[a[i]] = ++idcnt;
	for(RG int i = 1; i <= n; i++) idp[i] = ++idcnt;
	T = ++idcnt;
}

void build_edge()
{
	static bool vis[maxn]; clear(vis, 0); init_id();
	for(RG int i = 1; i <= n; i++)
		if(!vis[a[i]])
		{
			vis[a[i]] = true;
			add_edge(ida[a[i]], T, m * a[i] * a[i]);
		}
	for(RG int i = 1; i <= n; i++)
		add_edge(idp[i], ida[a[i]], INF),
		add_edge(idp[i], T, a[i]);
	for(RG int i = 1; i <= n; i++)
		for(RG int j = i; j <= n; j++)
		{
			if(g[i][j] > 0)
			{
				ans += g[i][j];
				add_edge(S, id[i][j], g[i][j]);
			}
			else if(g[i][j] < 0) add_edge(id[i][j], T, -g[i][j]);
			add_edge(id[i][j], idp[i], INF);
			add_edge(id[i][j], idp[j], INF);
			if(i != j)
				add_edge(id[i][j], id[i + 1][j], INF),
				add_edge(id[i][j], id[i][j - 1], INF);
		}
}

int main()
{
	clear(head, -1); n = read(), m = read();
	for(RG int i = 1; i <= n; i++) a[i] = read();
	for(RG int i = 1; i <= n; i++)
		for(RG int j = i; j <= n; j++) g[i][j] = read();
	build_edge();
	printf("%lld\n", ans - Dinic());
	return 0;
}

/**************** Dinic ******************/
inline int Dinic()
{
	int res = 0;
	while(bfs())
	{
		for(RG int i = S; i <= T; i++) cur[i] = head[i];
		res += dfs(S, INF);
	}
	return res;
}

int lev[maxn], q[maxn], tail;
bool bfs()
{
	clear(lev, 0);
	q[tail = lev[S] = 1] = S;
	for(RG int i = 1; i <= tail; i++)
	{
		int x = q[i];
		for(RG int j = head[x]; ~j; j = e[j].next)
		{
			int to = e[j].to; if(lev[to] || (!e[j].cap)) continue;
			q[++tail] = to, lev[to] = lev[x] + 1;
		}
	}
	return lev[T];
}

int dfs(int x, int f)
{
	if(x == T || (!f)) return f;
	int ans = 0, cap;
	for(RG int &i = cur[x]; ~i; i = e[i].next)
	{
		int to = e[i].to;
		if(e[i].cap && lev[to] == lev[x] + 1)
		{
			cap = dfs(to, std::min(f - ans, e[i].cap));
			e[i].cap -= cap, e[i ^ 1].cap += cap;
			ans += cap; if(ans == f) break;
		}
	}
	return ans;
}