백준 13992 Delight for a Cat

https://www.acmicpc.net/problem/13992

전혀 그렇게 보이지 않지만 MCMF 문제입니다… 그래프 모델링을 어떻게 할 수 있는지 살펴보도록 하겠습니다. MCMF 알고리즘은 여기서 보실 수 있습니다.

고양이가 잠을 자는 것을 base로 생각합시다. 우리는 고양이가 먹는 날만 고려하면 됩니다.

[변수 정리]

k: 주기

mn:고양이가 주기동안 먹어야 하는 최소 횟수(me),

mx: 고양이가 주기동안 먹어야 하는 최대 횟수(k-ms),

sc[i]: 고양이가 먹었을 때 느끼는 행복값,

ec[i]: 고양이가 잘 때 느끼는 행복값,

dif[i]=-(ec[i]-sc[i]): -(잠을 포기하고 먹을 때 느끼는 행복값)

[문제 세분화]

1. $ mn=mx=1 $ 인 경우

고양이가 주기 동안 한 번 식사할 수 있으므로 점화식을 구할 수 있습니다.

$ f(i)=max(f(i+1),f(i+k)-(-dif[i])) $

2. $ mn=0, 0 \leq mx $ 인 경우

주기(k) 동안 최대 mx개의 칸을 선택할 수 있을 떄 선택하는 방법의 문제와 동일합니다.

아직까지는 플로우 문제라고 유추가 되네요. ㅎㅎ.. src에서 mx의 유량을 흘려주고 각각의 유량들 중에서 최단거리(-dif)를 찾으면 됩니다. -dif를 cost로 하는 MCMF입니다.

[그래프 모델링]

Edge(capacity, cost)

A. 0(src1)에서 1(src2)로 Edge(mx,0) 연결

B. src2(1)에서 (2 … k+1)로 Edge(mx,0) 연결

C. i(2 … n-k+1)에서 i+k로 Edge(1,dif[i]) 연결

D. i(n-k+2 … n+1)에서 n+2(synk)로 Edge(1,dif[i]) 연결

E. i(2 … n+1) 에서 i+1 로 Edge(mx,0) 연결

3. $ 0 \leq mn,mx $ 인 경우

mx개의 유량이 흐르되 주기 안에서 mn 미만의 유량이 흐르는 것을 방지해야 합니다. 그래프 모델링만 바꿔서 해결할 수 있을까요? 해결할 수 있습니다!

E 간선에서 Edge(mx-mn,0) 을 연결해 봅시다. 자는 날짜가 최대 mx-mn으로 한정되기 때문에 먹는 날짜가 최소 mn으로 한정됩니다.

[최종 그래프 모델링]

A. 0(src1)에서 1(src2)로 Edge(mx,0) 연결

B. src2(1)에서 (2 … k+1)로 Edge(mx,0) 연결

C. i(2 … n-k+1)에서 i+k로 Edge(1,dif[i]) 연결

D. i(n-k+2 … n+1)에서 n+2(synk)로 Edge(1,dif[i]) 연결

E. i(2 … n+1) 에서 i+1 로 Edge(mx-mn,0) 연결

따라서 답은 (잠만 잤을 때 얻는 행복도-mcmf로 구해준 행복도의 총합)이 됩니다.

참고로, 경로는 먹는 간선인 C,D에 ID(3)을 붙인 후 C,D 간선에서 역간선의 플로우로 확인했습니다.

#include<bits/stdc++.h>
#define INF (ll)1e18
#define pb push_back
#define ff first
#define ss second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
const int SZ=1005;
ll sc[SZ];
ll ec[SZ];
ll dif[SZ];
ll eat[SZ];
struct MCMF{
	struct Edge{
		int to,rev,id; ll cap,cost;
		Edge(int to, int rev, int id, ll cap, ll cost):to(to),rev(rev),id(id),cap(cap),cost(cost){
		}
	};
	vector<vector<Edge>> graph;
	vector<pii> par;
	int n,src,snk;
	MCMF(int n, int src, int snk):n(n),src(src),snk(snk){
		graph.resize(n+5);
		par.resize(n+5);
	}
	void push_edge(int a, int b, int id, ll capa, ll cst){
		graph[a].pb(Edge(b,graph[b].size(),id,capa,cst));
		graph[b].pb(Edge(a,graph[a].size()-1,id,0,-cst));
	}
	bool SPFA(){
		vector<int> inq;
		vector<ll> dst;
		inq.resize(n+5,0);
		dst.resize(n+5,INF);		
		queue<int> q;
		q.push(src); inq[src]=1; dst[src]=0;
		while(!q.empty()){
			int h=q.front(); q.pop();
			inq[h]=0;
			for(int i=0;i<(int)graph[h].size();i++){
				auto e=graph[h][i];
				if(e.cap<=0) continue;
				if(dst[h]<INF && dst[e.to]>dst[h]+e.cost){
					dst[e.to]=dst[h]+e.cost; par[e.to]={h,i};
					if(inq[e.to]==0){
						q.push(e.to);
						inq[e.to]=1;
					}
				}
			}
		}
		return dst[snk]!=INF;
	}
	ll flow(int k){
		ll tot=0;
		while(SPFA()){
			ll c=0, f=INF;
			for(int i=snk;i!=src;i=par[i].ff){
				auto &e=graph[par[i].ff][par[i].ss];
				f=min(f,e.cap);
				c+=e.cost;
			}
			tot+=c*f;
			for(int i=snk;i!=src;i=par[i].ff){
				auto &e=graph[par[i].ff][par[i].ss];
				e.cap-=f;
				graph[e.to][e.rev].cap+=f;
			}
		}
		for(int i=k+2;i<=n+1;i++){
			for(auto e:graph[i]){
				if(e.id!=3) continue;
				if(e.to==i-k && e.cap>0){
					eat[e.to]=1;
				}
			}
		}
		for(auto e:graph[snk]){
			if(e.id!=3) continue;
			if(e.cap>0){
				eat[e.to]=1;
			}
		}		
		return tot;
	}
};
int main(void){
	ios_base::sync_with_stdio(false); cin.tie(NULL);
	int n,k,ms,me; cin>>n>>k>>ms>>me;
	int mn=me,mx=k-ms;
	ll ans=0;
	for(int i=2;i<=n+1;i++){
		ll x; cin>>x;
		sc[i]=x;
		ans+=x;
	}
	for(int i=2;i<=n+1;i++) cin>>ec[i];
	for(int i=2;i<=n+1;i++) dif[i]=sc[i]-ec[i];
	MCMF mf=MCMF(n,0,n+2);
	mf.push_edge(mf.src,1,1,mx,0);
	for(int i=2;i<=k+1;i++) mf.push_edge(1,i,2,INF,0);
	for(int i=2;i<=n+1;i++){
		int nxt=i+k>n+1?n+2:i+k;
		mf.push_edge(i,nxt,3,1,dif[i]);
	}
	for(int i=2;i<=n+1;i++) mf.push_edge(i,i+1,4,mx-mn,0);
	cout<<ans-mf.flow(k)<<'\n';
	for(int i=2;i<=n+1;i++){
		if(eat[i]) cout<<"E";
		else cout<<"S";
	}
	return 0;
}