2022-2023 ICPC East Central North America Regional Contest (ECNA 2022)

Preface

闲了两天没训练,今天又开始上班,结果唐得发昏后期也没题可写直接光速下班

只能说感觉老外的题目难度跨度都好大,easy确实简单,hard确实难,medium确实少


A. A-Mazing Puzzle

题目看起来很复杂,但仔细一想会发现有用的状态总数只有\(4n^2\)

即我们可以暴力记录下两个机器人的坐标,然后记下其中一个机器人的方向,就可以唯一确定一个状态了(因为两个机器人的方向的相对差距是不变的,因此记一个就行)

然后转移的话就直接写一个类似Dijkstra的东西即可,要注意各种细节

#include<bits/stdc++.h>
using namespace std;

using pii = pair<int, int>;
#define ft first
#define sd second

const int N = 55;
const int dir1[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
const int dir2[4][3] = {{0, 0, 0}, {1, 0, 0}, {0, 0, -1}, {1, -1, 0}};
const char pat[5] = "NESW";

int n, c, r, e;
int c1, r1, d1, c2, r2, d2;
bool wall[2][55][55];
int dis[52][52][52][52][2];
bool inD[52][52][52][52];

struct Node{
	int cc1, rr1, cc2, rr2;	
	pii d;
	bool operator<(const Node &b)const{return d > b.d;}
};


signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	memset(dis, 0x3f, sizeof(dis));
	char ch1, ch2;
	cin >> c >> r >> e;
	cin >> c1 >> r1 >> ch1 >> c2 >> r2 >> ch2;
	for (int i=0; i<4; ++i){
		if (ch1==pat[i]) d1=i;
		if (ch2==pat[i]) d2=i;
	}
	
	cin >> n;
	for (int i=1; i<=n; ++i){
		int c0, r0; cin >> c0 >> r0;
		wall[0][c0][r0]=true;
	}
	
	for (int i=1; i<=c; ++i) wall[0][i][0]=wall[0][i][r]=true;
	for (int i=1; i<=r; ++i) wall[1][0][i]=wall[1][c][i]=true;
	wall[0][e][0]=false;
	
	cin >> n;
	for (int i=1; i<=n; ++i){
		int c0, r0; cin >> c0 >> r0;
		wall[1][c0][r0]=true;
	}
	
	priority_queue<Node> que;
	que.push(Node{c1, r1, c2, r2, make_pair(0, 0)});
	dis[c1][r1][c2][r2][0]=0, dis[c1][r1][c2][r2][1]=0;
	
	while (!que.empty()){
		auto [cc1, rr1, cc2, rr2, pp] = que.top(); que.pop();
//		printf("cc1=%d rr1=%d cc2=%d rr2=%d pp(%d %d)\n", cc1, rr1, cc2, rr2, pp.ft, pp.sd);
		if (inD[cc1][rr1][cc2][rr2]) continue;
		inD[cc1][rr1][cc2][rr2]=true;
		
		for (int j=0; j<4; ++j){
			int bp=0;
			int nc1, nr1;
			auto [a1, b1, c1] = dir2[(j+d1)%4];
			if (wall[a1][cc1+b1][rr1+c1]) nc1=cc1, nr1=rr1, ++bp;
			else if (cc1==e && rr1==0) nc1=cc1, nr1=rr1;
			else nc1=cc1+dir1[(j+d1)%4][0], nr1=rr1+dir1[(j+d1)%4][1];
			
			int nc2, nr2;
			auto [a2, b2, c2] = dir2[(j+d2)%4];
			if (wall[a2][cc2+b2][rr2+c2]) nc2=cc2, nr2=rr2, ++bp;
			else if (cc2==e && rr2==0) nc2=cc2, nr2=rr2;
			else nc2=cc2+dir1[(j+d2)%4][0], nr2=rr2+dir1[(j+d2)%4][1];
			
			if (bp==2) continue;
			if (nc1==nc2 && nr1==nr2 && !(nc1==e && nr1==0)) continue;
			pii npp = make_pair(pp.ft+1, pp.sd+bp);
//			printf("nc1=%d nr1=%d nc2=%d nr2=%d, npp(%d %d)\n", nc1, nr1, nc2, nr2, npp.ft, npp.sd);
			
			if (npp < make_pair(dis[nc1][nr1][nc2][nr2][0], dis[nc1][nr1][nc2][nr2][1])){
				que.push(Node{nc1, nr1, nc2, nr2, npp});
				dis[nc1][nr1][nc2][nr2][0]=npp.ft;
				dis[nc1][nr1][nc2][nr2][1]=npp.sd;
//				if ((nc1==e && nr1==0) && (nc2==e && nr2==0)){
//					printf("FUCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\n");
//				}
			}
		}	
//			puts("");
	}
	cout << dis[e][0][e][0][0] << ' ' << dis[e][0][e][0][1] << '\n';
	
	return 0;
}	

B. A Musical Question

签到,直接暴力DP即可,二元组\((x,y)\)表示第一张唱片刻录长度为\(x\)的歌,第二张唱片刻录长度为\(y\)的歌的状态

#include<cstdio>
#include<iostream>
#include<vector>
#include<utility>
#include<algorithm>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=1005;
int n,m; bool vis[N][N];
int main()
{
	RI i; vector <pi> S; S.push_back(pi(0,0)); vis[0][0]=1;
	for (scanf("%d%d",&m,&n),i=1;i<=n;++i)
	{
		int w; scanf("%d",&w); vector <pi> NS;
		for (auto [x,y]:S)
		{
			if (x+w<=m&&!vis[x+w][y]) vis[x+w][y]=1,NS.push_back(pi(x+w,y));
			if (y+w<=m&&!vis[x][y+w]) vis[x][y+w]=1,NS.push_back(pi(x,y+w));
		}
		for (auto it:NS) S.push_back(it);
	}
	int ans1=0,ans2=0; for (auto [x,y]:S)
	if (x+y>ans1+ans2||(x+y==ans1+ans2&&abs(x-y)<abs(ans1-ans2))) ans1=x,ans2=y;
	return printf("%d %d",max(ans1,ans2),min(ans1,ans2)),0;
}

C. Cribbage On Steroids

首先pair的贡献很好算,然后顺子的话只要枚举顺子的起点和终点即可快速算贡献

至于15's的话可以直接大力DP,设\(f_{i,j}\)表示用前\(i\)种点数的牌组成价值和为\(j\)的牌面的方案数,转移很trivial

#include<bits/stdc++.h>
using namespace std;
#define int long long

int C[105][105];
int n, cnt[20];
int sum[20];
int f[20][20];

signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	C[0][0]=C[1][0]=C[1][1]=1;
	for (int i=2; i<105; ++i){
		C[i][0]=C[i][i]=1;
		for (int j=1; j<i; ++j)	C[i][j]=C[i-1][j]+C[i-1][j-1];
	}
//	for (int i=0; i<=100; ++i) printf("C[100][%d]=%lld\n", i, C[100][i]);
	cin >> n;
	for (int i=1; i<=n; ++i){
		char ch; cin >> ch;
		if ('T'==ch) ++cnt[10];
		else if ('J'==ch) ++cnt[11];
		else if ('Q'==ch) ++cnt[12];
		else if ('K'==ch) ++cnt[13];
		else if ('A'==ch) ++cnt[1];
		else ++cnt[ch-'0'];
	}
	int ans=0;
	for (int i=1; i<=13; ++i){
		sum[i] += sum[i-1]+i*cnt[i];
	}	
	for (int i=1; i<=13; ++i){
		if (cnt[i]>=2) ans += cnt[i]*(cnt[i]-1);
	}
//	printf("ans=%lld\n", ans);
	
	int lst=0, res=1;
	for (int i=1; i<=14; ++i){
		if (0==cnt[i]){
			if (i-lst-1>=3) ans += res*(i-lst-1);
			res=1; lst=i;
		}else res*=cnt[i];
	}
//	printf("ans=%lld\n", ans);
	cnt[10]+=cnt[11];
	cnt[10]+=cnt[12];
	cnt[10]+=cnt[13];
	
	f[0][0]=1;
	for (int i=1; i<=10; ++i){
		for (int j=0; j<=15; ++j){
			for (int k=0; k<i; ++k){
				for (int w=1; w<=cnt[i]; ++w){
					if (w*i>j) break;
					f[i][j]	+= f[k][j-w*i]*C[cnt[i]][w];
				}
			}
		}
	}	
	for (int i=1; i<=10; ++i) ans += f[i][15]*2;
	cout << ans << '\n';
	
	return 0;
}	

D. Determining Nucleotide Assortments

签到,注意不要搞错ATGC的顺序

#include<bits/stdc++.h>
using namespace std;

const int N = 5e4+5;
using pii = pair<int, int>;
#define ft first
#define sd second

int n, q;
int cnt[4][N];
char pat[5] = "ATGC";
char str[N];

signed main(){
	scanf("%s", str+1);
	n = strlen(str+1);
	for (int i=1; i<=n; ++i){
		for (int j=0; j<4; ++j){
			if (str[i]==pat[j]) ++cnt[j][i];	
		}
	}
	for (int i=1; i<=n; ++i){
		for (int j=0; j<4; ++j){
			cnt[j][i] += cnt[j][i-1];	
		}
	}
	scanf("%d", &q);
	while (q--){
		int l, r; scanf("%d%d", &l, &r);
		vector<pii> vec;
		for (int j=0; j<4; ++j) vec.push_back(make_pair(cnt[j][r]-cnt[j][l-1], j));
		sort(vec.begin(), vec.end(), [&](pii a, pii b){return a.ft!=b.ft ? a.ft>b.ft : a.sd<b.sd;});
		for (auto [a, b] : vec) putchar(pat[b]);
		puts("");
	}
	
	return 0;	
}

E. Hilbert's Hedge Maze

因为前面我太唐了没给徐神留出足够的时间,红豆泥私密马赛

这题徐神给我讲了做法我也没太懂,只能说笨比脑子是这样的,只能坐等徐神补题了

Upt:徐神光速补完了这题,ORZ

#include <bits/stdc++.h>

using llsi = long long signed int;

struct vivi {
    llsi x, y;
    inline vivi operator +(const vivi &b) const { return {x + b.x, y + b.y}; }
    inline vivi operator -(const vivi &b) const { return {x - b.x, y - b.y}; }
    inline vivi rotl(const vivi &b) const {
        return {b.x - (y - b.y), b.y + (x - b.x)};
    }
    inline vivi rotr(const vivi &b) const {
        return {b.x + (y - b.y), b.y - (x - b.x)};
    }
    inline llsi dis(const vivi &b) const {
        return std::abs(x - b.x) + std::abs(y - b.y);
    }
    inline bool operator ==(const vivi &b) const {
        return x == b.x && y == b.y;
    }
};

struct msg {
    llsi n; //  l, r, d, t;
    vivi from, to, hibiki;
};

vivi hikari(llsi n, vivi pos, /*llsi l, llsi r, llsi d, llsi t,*/ std::vector<msg> &st) {
    // std::cerr << "hikari(" << n << ", (" << pos.x << ", " << pos.y << "), " /*<< l << ", " << r << ", " << d << ", " << t << ", "*/ << "[std::vector<msg>])\n";
    if(pos.x <= 0ll || pos.x >= (1ll << n) || pos.y <= 0ll || pos.y >= (1ll << n)) return pos;
    if(n == 1) {
        st.push_back(msg{n, /*l, r, d, t,*/ pos, pos - vivi{0, 1ll}, pos});
        return pos - vivi{0, 1ll};
    }
    /* llsi xmid = (l + r) >> 1ll, ymid = (d + t) >> 1ll; */
    
    vivi from, to, hibiki;
    const vivi R = {1ll << n - 2, 1ll << n - 2};
    if(pos.x == (1ll << n - 1) || pos.y == (1ll << n - 1)) from = pos;
    if(pos.x < (1ll << n - 1) && pos.y < (1ll << n - 1)) from = hikari(n - 1, pos.rotl(R)                             , /*  l, xmid  ,   d, ymid  ,*/ st).rotr(R);
    if(pos.x > (1ll << n - 1) && pos.y < (1ll << n - 1)) from = hikari(n - 1, (pos - vivi{1ll << n - 1, 0ll}).rotr(R) , /*xmid + 1, r,   d, ymid  ,*/ st).rotl(R) + vivi{1ll << n - 1, 0ll}; 
    if(pos.x < (1ll << n - 1) && pos.y > (1ll << n - 1)) from = hikari(n - 1, pos - vivi{0ll         , 1ll << n - 1}  , /*  l, xmid  , ymid + 1, t,*/ st) + vivi{0ll         , 1ll << n - 1};
    if(pos.x > (1ll << n - 1) && pos.y > (1ll << n - 1)) from = hikari(n - 1, pos - vivi{1ll << n - 1, 1ll << n - 1}  , /*xmid + 1, r, ymid + 1, t,*/ st) + vivi{1ll << n - 1, 1ll << n - 1};

    if(pos.x < (1ll << n - 1) && pos.y < (1ll << n - 1)) for(auto &[_, from, to, hibiki]: st) from = from.rotr(R)                            , to = to.rotr(R)                            , hibiki = hibiki.rotr(R)                            ;
    if(pos.x > (1ll << n - 1) && pos.y < (1ll << n - 1)) for(auto &[_, from, to, hibiki]: st) from = from.rotl(R) + vivi{1ll << n - 1, 0ll}  , to = to.rotl(R) + vivi{1ll << n - 1, 0ll}  , hibiki = hibiki.rotl(R) + vivi{1ll << n - 1, 0ll}  ;
    if(pos.x < (1ll << n - 1) && pos.y > (1ll << n - 1)) for(auto &[_, from, to, hibiki]: st) from = from + vivi{0ll          , 1ll << n - 1}, to = to + vivi{0ll          , 1ll << n - 1}, hibiki = hibiki + vivi{0ll          , 1ll << n - 1};
    if(pos.x > (1ll << n - 1) && pos.y > (1ll << n - 1)) for(auto &[_, from, to, hibiki]: st) from = from + vivi{1ll << n - 1 , 1ll << n - 1}, to = to + vivi{1ll << n - 1 , 1ll << n - 1}, hibiki = hibiki + vivi{1ll << n - 1 , 1ll << n - 1};
    
    // std::cerr << "from = (" << from.x << ", " << from.y << ")\n";

    if(from.x <= 0ll || from.x >= (1ll << n) || from.y <= 0ll || from.y >= (1ll << n)) return from;
    
    if(from.y <= (1ll << n - 1)) to = vivi{1ll << (n - 1), 0}       , hibiki = vivi{1ll << (n - 1), 1ll};
    else                         to = vivi{1ll << (n - 1), 1ll << n}, hibiki = vivi{1ll << (n - 1), (1ll << n) - 1};

    // std::cerr << "to = (" << to.x << ", " << to.y << ")\n";

    st.push_back(msg{n, /*l, r, d, t,*/ from, to, hibiki});
    return to;
}

int main(void) {
    std::ios::sync_with_stdio(false);
    int t; std::cin >> t; while(t--) {
        llsi n; vivi A, B;
        std::cin >> n >> A.x >> A.y >> B.x >> B.y;
        A = A + vivi{1ll, 1ll}; B = B + vivi{1ll, 1ll};
        std::vector<msg> as, bs;
        A = hikari(n, A, /*1, 1ll << n, 1, 1ll << n,*/ as);
        // std::cerr << "debug as: \n";
        // for(auto [n, from, to, hibiki]: as) std::cerr << n << " (" << from.x << ", " << from.y << "), (" << hibiki.x << ", " << hibiki.y << ")\n";
        B = hikari(n, B, /*1, 1ll << n, 1, 1ll << n,*/ bs);
        // std::cerr << "debug bs: \n";
        // for(auto [n, from, to, hibiki]: bs) std::cerr << n << " (" << from.x << ", " << from.y << "), (" << hibiki.x << ", " << hibiki.y << ")\n";
        size_t i = 0, j = 0;
        llsi ans = 0;
        while(i < as.size() || j < bs.size()) {
            if(i < as.size() && j < bs.size() &&
                as[i].n == bs[j].n &&
                as[i].hibiki == bs[j].hibiki
            ) {
                ans += as[i].from.dis(bs[j].from);
                A = B;
                break;
            }
            if(j == bs.size() || i < as.size() && as[i].n < bs[j].n) ans += as[i].from.dis(as[i].to), i += 1;
            else                                                     ans += bs[j].from.dis(bs[j].to), j += 1;
        }
        llsi U = 1ll << n;
        if(!(A == B)) {
            if(A.y > 0 && A.y < U && B.y > 0 && B.y < U && (A.x <= 0 && B.x >= U || A.x >= U && B.x <= 0))
                ans += std::min(
                    U - A.y + U - B.y + std::abs(A.x - B.x),
                    A.y - 0 + B.y - 0 + std::abs(A.x - B.x)
                );
            else
            if(A.x > 0 && A.x < U && B.x > 0 && B.x < U && (A.y <= 0 && B.y >= U || A.y >= U && B.y <= 0))
                ans += std::min(
                    U - A.x + U - B.x + std::abs(A.y - B.y),
                    A.x - 0 + B.x - 0 + std::abs(A.y - B.y)
                );
            else
                ans += A.dis(B);
        }
        std::cout << ans << char(10);
    }
    return 0;
}


F. It's About Time

大力枚举+小推式子,对于徐神来说就是洒洒水

#include <bits/stdc++.h>

int main() {
    const long double pi = std::acos(-1.L);
    long double r, s, h;
    std::cin >> r >> s >> h;
    const long double tyear = 2.L * pi * r / (s * h);
    const long double cy = std::round(tyear);
    const long double ly = cy < tyear ? cy + 1.L : cy - 1.L;
    long double mindelta = 1e9;
    std::array<int, 3> ans;
    for(int n1 = 2; n1 < 1000; ++n1) {
        for(int n2 = n1 + n1; n2 < 1000; n2 += n1) {
            for(int n3 = n2 + n2; n3 <= 1000; n3 += n2) {
                const int l = n3 / n1 - n3 / n2 + n3 / n3;
                const int c = n3 - l;
                const long double aver = (l * ly + c * cy) / (long double)n3;
                if(std::abs(aver - tyear) < mindelta) {
                    mindelta = std::abs(aver - tyear);
                    ans = {n1, n2, n3};
                }
            } 
        }
    }
    std::cout << ans[0] << ' ' << ans[1] << ' ' << ans[2] << std::endl;
    return 0;
}

G. Pea Pattern

这种乍一看不知道有什么好做法,但是一看榜被过穿了的题,首先考虑大力出奇迹

直觉告诉我们这个序列做到后面很快就会产生重复,因此如果有解的话直接暴力模拟即可

#include<cstdio>
#include<iostream>
#include<set>
#include<string>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
string n,m;
int main()
{
	RI i; cin>>n>>m; set <string> ext;
	if (n.size()>100||m.size()>100) return puts("I'm bored."),0;
	for (int cnt=1;;++cnt)
	{
		if (n==m) return printf("%d",cnt),0;
		if (ext.count(n)) break; ext.insert(n);
		static int c[10]; memset(c,0,sizeof(c));
		for (i=0;i<n.size();++i) ++c[n[i]-'0'];
		string t=""; for (i=0;i<10;++i) if (c[i]) t+=to_string(c[i])+char('0'+i); n=t;
	}
	return puts("Does not appear"),0;
}

H. Picking Up Steam

好难的几何,大致有个思路但貌似细节很多不太好写


I. Road To Savings

祁神写的,我题目都没看

#include<bits/stdc++.h>
using namespace std;

using pii = pair<int, int>;
const int INF = (int)1e8+5;
const int N = 205;

int n, m, src, dr;
int dis[N][N];
struct Edge{int a, b, w;};
vector<Edge> G;

signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	cin >> n >> m >> src >> dr;
	for (int i=1; i<=n; ++i){
		for (int j=i+1; j<=n; ++j){
			dis[i][j] = dis[j][i] = INF;
		}
	}
	for (int i=1; i<=m; ++i){
		int a, b, w; cin >> a >> b >> w;
		dis[a][b] = dis[b][a] = w;
		G.push_back(Edge{a, b, w});
	}
	for (int k=1; k<=n; ++k){
		for (int i=1; i<=n; ++i){
			for (int j=1; j<=n; ++j){
				if (dis[i][k]+dis[k][j]<dis[i][j]) dis[j][i]=dis[i][j] = dis[i][k]+dis[k][j];
			}
		}
	}
	int dist = dis[src][dr];
	int ans=0;
//	
//	for (int i=1; i<=n; ++i){
//		for (int j=1; j<=n; ++j){
//			printf("%d ", dis[i][j]);
//		}puts("");
//	}
	
	
	for (auto [a, b, w] : G){
//		printf("(%d %d) %d %d\n", a, b, dis[src][a]+dis[b][dr], dis[src][b]+dis[a][dr]);
		if (dis[src][a]+dis[b][dr]+w > dist && dis[src][b]+dis[a][dr]+w > dist) ans += w;
	}
	cout << ans;
	return 0;
}	

J. Simple Solitaire

徐神写的,我题目都没看(怎么又是打牌啊)

#include <bits/stdc++.h>

bool shrink(std::vector<std::string> &s) {
    // std::cerr << "Before: ";
    // for(int i = 0; i < s.size(); ++i) std::cerr << s[i] << char(i == s.size() - 1 ? 10 : 32);
    // std::cerr << std::flush;
    int pos = -1, type = 0;
    for(int i = s.size() - 1; i >= 3; --i) {
        if(s[i][0] == s[i - 3][0] && type < 4)
            pos = i, type = 4;
        if(s[i][1] == s[i - 3][1] && type < 2)
            pos = i, type = 2;
    }
    if(type == 4)
        s.erase(s.begin() + (pos - 3), s.begin() + (pos + 1));
    if(type == 2)
        s.erase(s.begin() + (pos - 3)),
        s.erase(s.begin() + (pos - 1));
    // std::cerr << "After: ";
    // for(int i = 0; i < s.size(); ++i) std::cerr << s[i] << char(i == s.size() - 1 ? 10 : 32);
    // std::cerr << std::flush;
    return type > 0;
}

int main() {
    std::vector<std::string> deck(52), st;
    for(auto &card: deck) std::cin >> card;
    for(auto card: deck) {
        st.push_back(card);
        while(shrink(st));
    }
    std::cout << st.size();
    for(int i = 0; i < st.size(); ++i) std::cout << char(32) << st[i];
    std::cout << std::endl;
    return 0;
}

K. Two Charts Become One

因为没判两个树都是单点的Corner Case红温了好久,浪费了好多时间

根据题目中对于树的同构的定义,其实只要判每个点的儿子按顺序重排后是否相同即可

更方便的写法是直接把每条边按照父亲到儿子的顺序加入集合中,最后比较两个数对应的边集合是否相同即可

然后注意下模拟建树的过程即可,由于只有一个点的时候不存在边因此可以假设一个编号为\(0\)的虚点套在最外层

#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<stack>
#include<set>
#include<cctype>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
string A,B;
inline vector <pi> solve(string s)
{
	RI i,j; string t=""; vector <pi> edges;
	for (i=0;i<s.size();++i) if (!isspace(s[i])) t+=s[i];
	t="(0("+t+"))"; stack <int> stk; set <pi> rst;
	for (i=0;i<t.size();++i)
	{
		if (t[i]=='(') { stk.push(i); continue; }
		if (t[i]==')')
		{
			int pos=stk.top(); stk.pop();
			int x=0; for (j=pos+1;;++j)
			if (isdigit(t[j])) x=x*10+t[j]-'0'; else break;
			for (auto it=rst.upper_bound(pi(pos,0));it!=rst.end();it=rst.erase(it))
			edges.push_back(pi(x,it->se));
			rst.insert(pi(pos,x)); continue;
		}
	}
	sort(edges.begin(),edges.end());
	return edges;
}
int main()
{
	ios::sync_with_stdio(false); cin.tie(0);
	getline(cin,A); getline(cin,B);
	return puts(solve(A)==solve(B)?"Yes":"No"),0;
}

L. Which Warehouse?

很裸的一个题

由于每个点不能选两种及以上的物资,因此我们可以把问题转化为一个二分图最小权匹配问题

左边\(n\)个点代表原图中的点,右边\(m\)个点代表物资,两点间连边对应将所有该类物资移动到对应点上的代价和,这个可以预处理最短路后快速计算

然后直接上KM即可,注意KM要求二分图两边点的数量相同,这点也很好解决,直接在右边多开\(n-m\)个点,表示对应点不作为存储地点,与左边所有点的边权都是\(0\)即可

#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=105,INF=1e18;
int n,m,c[N][N],dis[N][N];
namespace KM
{
	int w[N][N],kx[N],ky[N],py[N],vy[N],slk[N],pre[N];
	inline int solve(CI n)
	{
		RI i,j,k; for (i=1;i<=n;++i) for (j=1;j<=n;++j) kx[i]=max(kx[i],w[i][j]);
		for (i=1;i<=n;++i)
		{
			for (j=0;j<=n;++j) vy[j]=pre[j]=0,slk[j]=INF;
			int k=0,p=-1; for (py[k=0]=i;py[k];k=p)
			{
				int d=INF; vy[k]=1; int x=py[k];
				for (j=1;j<=n;++j) if (!vy[j])
				{
					int t=kx[x]+ky[j]-w[x][j];
					if (t<slk[j]) slk[j]=t,pre[j]=k;
					if (slk[j]<d) d=slk[j],p=j;
				}
				for (j=0;j<=n;++j)
				if (vy[j]) kx[py[j]]-=d,ky[j]+=d; else slk[j]-=d;
			}
			for (;k;k=pre[k]) py[k]=py[pre[k]];
		}
		int ret=0; for (i=1;i<=n;++i) ret+=kx[i]+ky[i];
		return -ret;
	}
};
using namespace KM;
signed main()
{
	RI i,j,k; for (scanf("%lld%lld",&n,&m),i=1;i<=n;++i)
	for (j=1;j<=m;++j) scanf("%lld",&c[i][j]);
	for (i=1;i<=n;++i) for (j=1;j<=n;++j)
	if (scanf("%lld",&dis[i][j]),dis[i][j]==-1) dis[i][j]=INF;
	for (k=1;k<=n;++k) for (i=1;i<=n;++i) for (j=1;j<=n;++j)
	dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	for (i=1;i<=n;++i) for (j=1;j<=m;++j)
	{
		int cur=0; for (k=1;k<=n;++k) cur+=dis[k][i]*c[k][j];
		w[i][j]=-cur;
	}
	return printf("%lld",solve(n)),0;
}

Postscript

每天被花式腐乳,真是菜菜又鸡鸡啊

posted @ 2024-02-02 19:07  空気力学の詩  阅读(85)  评论(0编辑  收藏  举报