题解 [PR #5] 双向奔赴

传送门

发送时间:2022-07-24 12:14:38
【PR #5】双向奔赴
原题保证了 "For all pairs of distinct integers \(1 \leqslant i, j \leqslant N\) , either \(a_{i,j} = a_{j,i} = −1\) or
\(0 \leqslant a_{i,j} , a_{j,i} \leqslant 10^6\)."
而在本题题面中仅保证了 \(-1\leqslant a_{i, j}\leqslant 10^6\)
因此读错题的我提交了一份实际上并不符合原题数据范围的 hack 并 hack 掉了若干份其实正确的代码
(这份 hack 中存在 \(a_{i, j}=-1\)\(a_{j, i}\geqslant 0\) 的情况)
因此请求撤销我提交的这份 hack,并(也许?)修改题面中的数据范围
麻烦您啦 /kk

乐。想找个 std 对拍发现好几份码跑得都不一样然后交了个 hack
然后接着拍发现 std 跑出来的数十分奇怪
然后去翻原题发现限制没给全

发现要给边定向使得原图形成强连通分量
那么考虑每次向当前强连通分量 \(s\) 中加入一条路径 \(a_1, a_2, \cdots, a_k\),其中 \(a_1, a_k\in s\)
考虑状压 + DP 来做这个事情
\(f_{s, u, w}\) 为当前强连通分量和走了一半的路径形成的点集为 \(s\),当前在 \(u\),路径终点为 \(w\)
考虑一些边界,比如 \(u=w\) 时一条边不能被同时定为两个方向,所以加一维 \(0/1\) 表示下一条边能否连向 \(w\)
再加一个辅助数组 \(g_s\) 表示强连通分量为 \(s\) 的最小代价
复杂度 \(O(n^32^n)\)

附两组数据:

3
-1 -1 10
-1 -1 10
10 10 -1

5
-1 -1 2 -1 8
-1 -1 7 -1 8
5 6 -1 2 1
-1 -1 4 -1 -1
9 8 4 -1 -1

输出好像都是 -1 吧不记得了

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int mp[20][20], dlt;
int f[1<<18][18][18][2], g[1<<18];
inline void chkmin(int& a, int b) {a=min(a, b);}

signed main()
{
	n=read();
	memset(f, 0x3f, sizeof(f));
	memset(g, 0x3f, sizeof(g));
	for (int i=0; i<n; ++i)
		for (int j=0; j<n; ++j)
			mp[i][j]=read();
	for (int i=0; i<n; ++i)
		for (int j=0,tem; j<n; ++j) if (~mp[i][j] && ~mp[j][i])
			tem=min(mp[i][j], mp[j][i]), mp[i][j]-=tem, mp[j][i]-=tem, dlt+=tem;
	// cout<<"---mp---"<<endl;	for (int i=0; i<n; ++i) {for (int j=0; j<n; ++j) cout<<setw(2)<<mp[i][j]<<' '; cout<<endl;}
	int lim=1<<n;
	g[1]=0;
	for (int s=0; s<lim; ++s) {
		// cout<<"s: "<<bitset<5>(s)<<endl;
		for (int u=0; u<n; ++u) if (s&(1<<u))
			for (int v=0; v<n; ++v) if (~mp[u][v])
				for (int w=0; w<n; ++w) if (s&(1<<w) && (!(s&(1<<v))||v==w)) {
					chkmin(f[s|(1<<v)][v][w][u!=w], f[s][u][w][1]+mp[u][v]);
					// printf("at f["); cout<<bitset<5>(s); printf("][%d][%d][%d], %d\n", u, w, 1, f[s][u][w][1]);
					// printf("chkmin2: f["); cout<<bitset<5>(s|(1<<v)); printf("][%d][%d][%d], %d\n", v, w, 1, f[s][u][w][1]+mp[u][v]);
					if (v!=w) {
						chkmin(f[s|(1<<v)][v][w][1], f[s][u][w][0]+mp[u][v]);
						// printf("chkmin3: f["); cout<<bitset<5>(s|(1<<v)); printf("][%d][%d][%d], %d\n", v, w, 1, f[s][u][w][0]+mp[u][v]);
					}
				}
		for (int i=0; i<n; ++i)
			chkmin(g[s], f[s][i][i][1]);
		for (int u=0; u<n; ++u) if (s&(1<<u))
			for (int v=0; v<n; ++v) if (~mp[u][v] && !(s&(1<<v)))
				for (int w=0; w<n; ++w) if (s&(1<<w)) {
					chkmin(f[s|(1<<v)][v][w][u!=w], g[s]+mp[u][v]);
					// printf("chkmin1: f["); cout<<bitset<5>(s|(1<<v)); printf("][%d][%d][%d], %d\n", v, w, u!=w, g[s]+mp[u][v]);
				}
	}
	printf("%d\n", g[lim-1]==INF?-1:dlt+g[lim-1]);

	return 0;
}
posted @ 2022-07-24 11:45  Administrator-09  阅读(3)  评论(0编辑  收藏  举报