- 题目链接:https://codeforces.com/contest/1430/problem/G
- 这个题目可以采用状态压缩DP求解,最简单的是O(n*3^n)的暴力状态压缩DP,但是复杂度是不允许的,优化的也不会。这里参考了这篇博客,采用了最小割的建模方式,求解这个题目(https://www.luogu.com.cn/problem/solution/CF1430G)
这里提供一个O(n*3^n)的状态压缩DP
//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
return a / gcd(a, b) * b;
}
int n , m;
const int N = 2e5 + 5;
int ve[1<<20];
ll a[1<<20], sum[1<<20];
vector <int> edge[N];
int dp[20][1<<20], f[20][1<<20], ans[20];
int main()
{
read (n, m);
for (int i = 1 ; i <= m ; i ++)
{
int u , v, w;
read (u, v , w);
u --;
v --;
edge[u].pb (v);
a[u] += w;
a[v] -= w;
}
for (int i = 0 ; i < (1 << n) ; i ++)
{
for (int j = 0 ; j < n ; j ++)
{
if ((i >> j) & 1)
sum[i] += a[j];
}
for (int j = 0 ; j < n ; j ++)
{
if ((i >> j) & 1)
for (auto v : edge[j])
ve[i] |= (1 << v);
}
}
mem (dp, INF);
dp[0][0] = 0;
for (int i = 1 ; i <= n ; i ++)
{
for (int s = 0 ; s < (1<<n) ; s ++)
{
for (int s1 = s ; s1 ; s1 = (s1 - 1) & s)
{
int s2 = (s ^ s1);
if ((ve[s1] & s2) == ve[s1])
{
int cost = dp[i-1][s2] + sum[s1] * (i - 1);
if (cost < dp[i][s])
{
dp[i][s] = cost;
f[i][s] = s1;
}
}
}
}
}
int s = (1 << n) - 1;
int index = n;
for (int i = 1 ; i <= n ; i ++)
{
if (dp[i][s] < dp[index][s])
index = i;
}
for (int i = index ; i >= 1 ; i --)
{
int s1 = f[i][s];
for (int j = 0 ; j < n ; j ++)
{
if ((s1 >> j) & 1)
ans[j] = i - 1;
}
s = s ^ s1;
}
for (int i = 0 ; i < n ; i ++) write (ans[i]), SP;
}
这里是最小割:
//#define LOCAL
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
#define mem(a, b) memset(a,b,sizeof(a))
#define DNF 0x7f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define pb push_back
#define LF putchar('\n')
#define SP putchar(' ')
#define p_queue priority_queue
#define CLOSE ios::sync_with_stdio(0); cin.tie(0)
template<typename T>
void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f *= -1;ch = getchar();}while(isdigit(ch)){x = x * 10 + ch - 48; ch = getchar();}x *= f;}
template<typename T, typename... Args>
void read(T &first, Args& ... args) {read(first);read(args...);}
template<typename T>
void write(T arg) {T x = arg;if(x < 0) {putchar('-'); x =- x;}if(x > 9) {write(x / 10);}putchar(x % 10 + '0');}
template<typename T, typename ... Ts>
void write(T arg, Ts ... args) {write(arg);if(sizeof...(args) != 0) {putchar(' ');write(args ...);}}
using namespace std;
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) {
return a / gcd(a, b) * b;
}
const int N = 1e6 + 5;
int id[25][25];
int head[N] , cnt = 0 , tot = 0;
struct node {
int t, c, next;
}edge[N];
void add (int f, int t, int c)
{
edge[cnt].t = t;
edge[cnt].c = c;
edge[cnt].next = head[f];
head[f] = cnt ++;
}
void addedge (int f, int t, int c)
{
add (f, t, c);
add (t, f, 0);
}
const int INF = 1ll<<60;
int n , m , ed , st, cur[N], level[N];
ll a[N];
int vis[N];
void dfs2 (int u)
{
vis[u] = 1;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
if (edge[i].c && !vis[edge[i].t])
dfs2 (edge[i].t);
}
}
bool DinicBfs(int s, int e) //利用广搜给网络图分层
{
mem(level,0);
queue<int>q;
q.push(s);
level[s] = 1;
while(!q.empty())
{
int root = q.front();
q.pop();
for(int i = head[root] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].t, c = edge[i].c;
if(!level[v] && c > 0)
{
level[v] = level[root] + 1;
q.push(v);
}
}
}
return level[e] != 0; //如果当前网络能到达汇点说明存在增广路
}
int DinicDfs(int root , int flow) //利用DFS在已经分好层的网络中寻找增广路
{
if(root == ed)
return flow;
int newflow , cost = 0;
for(int& i = cur[root] ; i != -1 ; i = edge[i].next) //当前弧优化,&i代表i改动cur也会改动
{
int v = edge[i].t, c = edge[i].c;
if(level[v] == level[root] + 1 && c > 0 && (newflow = DinicDfs(v,min(c,flow-cost))))//多路增广flow-cost
{
cost += newflow;
edge[i].c -= newflow;
edge[i^1].c += newflow;
if(cost == flow) break;
}
}
return cost; //返回当前分层网络中能够寻找到的最大流
}
int Dinic()
{
int ans = 0;
while (DinicBfs(st, ed))
{
for (int i = st ; i <= tot ; i ++) //如果顶点标号为0,则从0开始
cur[i] = head[i];
while (int add = DinicDfs(st, INF))
ans += add;
}
return ans;
}
signed main()
{
mem (head, -1);
cin >> n >> m;
st = ++ tot , ed = ++ tot;
for (int i = 1 ; i <= n ; i ++)
for (int j = 0 ; j <= n ; j ++)
id[i][j] = ++ tot;
for (int i = 1 ;i <= m ; i ++)
{
int u, v, w;
cin >> u >> v >> w;
a[u] += w;
a[v] -= w;
for (int j = 0 ; j < n ; j ++)
addedge (id[v][j], id[u][j+1], INF);
}
for (int i = 1 ; i <= n ; i ++)
{
addedge (st, id[i][0], INF);
addedge (id[i][n], ed, INF);
for (int j = 0 ; j < n ; j ++)
addedge (id[i][j] , id[i][j+1], a[i] * j + 4e11);
}
Dinic();
dfs2(st);
for (int i = 1 ; i <= n ; i ++)
{
for (int j = n ; j >= 0 ; j --)
if (vis[id[i][j]])
{
cout << j << ' ';
break;
}
}
cout << endl;
}

本文介绍了一道Codeforces竞赛题目的两种解法:一种是状态压缩动态规划,复杂度较高;另一种是通过构建最小割模型求解最优方案。文章提供了完整的代码实现,并对比了两者的优劣。
880

被折叠的 条评论
为什么被折叠?



