yesnomaybe's blog

By yesnomaybe, history, 4 years ago, In English

Question link : https://atcoder.jp/contests/abc175/tasks/abc175_f and My Solution link : https://atcoder.jp/contests/abc175/submissions/15984070

Solution reference of int65536 (rank 1 in contest): https://atcoder.jp/contests/abc175/submissions/15936454

I did some sort of similar approach but instead of applying Dijkstra's algorithm to find the shortest path where state is defined using (prefix & suffix), I tried to use recursive dp approach with memoization (seemed intuitive to me).

I manage to pass almost all the cases but getting RE in 4 test cases. Can someone please help me where I might be going wrong with my solution?

Also can someone share an insight why every high rated coder intuitively thought of applying Dijkstra and not a recursive dp? Is there some property that I am missing? Can someone please help?

For reference : - tribute_to_Ukraine_2022 https://mirror.codeforces.com/blog/entry/81458?#comment-681148 and - galen_colin https://www.youtube.com/watch?v=HmbZiI0rAk4

My Solution :

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

#define LL long long

const int MOD = 1e9 + 7;
const LL INF = 1e18 + 9;
const int MX = 1e5 + 5;

int n;
vector<string> inp;
vector<LL> cost;

bool isPalindrome(string s) {
	int l = s.size();
	for(int i=0;i<l;i++) {
		if(s[i] != s[l-1-i])
			return false;
	}
	return true;
}

vector<string> getNextState(string s1, string s2) {
	vector<string> ret;

	int l1 = s1.size();
	int l2 = s2.size();
	int l = min(l1,l2);
	for(int i=0;i<l;i++) {
		if(s1[i] != s2[l2-1-i]) {
			return ret;
		}
	}
	if(l1 == l2) {
		ret.push_back("");
		ret.push_back("");
	}	
	else if(l1 > l2) {
		string tmp = s1.substr(l2);
		ret.push_back(tmp);
		ret.push_back("");
	}
	 else {
		string tmp = s2.substr(0, l2-l1);
		ret.push_back("");
		ret.push_back(tmp);
	}
	return ret;
} 

map<pair<string, string>, LL > cache;

LL solve(string pre, string suff) {
	if(isPalindrome(pre + suff)) {
		return 0;
	}

	if(cache.find({pre,suff}) != cache.end()) {
		return cache[{pre,suff}];
	}

	LL ret = INF;

	for(int i=0;i<n;i++) {
		vector<string> cand;
		if(pre.size() > 0) {
			cand = getNextState(pre, inp[i]);
		} else {
			cand = getNextState(inp[i], suff);
		}

		if(cand.size() == 0) {
			continue;
		}

		ret = min(ret, cost[i] + solve(cand[0], cand[1]));
	}

	cache[{pre, suff}] = ret;
	return ret;
}

int main() {
	cin >> n;
	inp.resize(n);
	cost.resize(n);
	for(int i=0;i<n;i++) {
		cin >> inp[i] >> cost[i];
	}

	LL ans = INF;
	for(int i=0;i<n;i++) {
		ans = min(ans, cost[i] + solve(inp[i], ""));
	}
	if(ans == INF) {
		cout << -1 << endl;
		return 0;
	}
	cout << ans << endl;

	return 0;
}

  • Vote: I like it
  • +18
  • Vote: I do not like it

»
4 years ago, # |
Rev. 2   Vote: I like it 0 Vote: I do not like it

I am sorry, but why downvotes? Can someone please help me with my solution and in general the intuition behind the problem?

»
4 years ago, # |
Rev. 2   Vote: I like it +10 Vote: I do not like it

I guess the reason for downvotes is me not explaining my code. So, I'll explain it. I am using the same idea as mentioned by I_love_chickpea's solution (mentioned above). But instead of Dijkstra, I opted for recursive dp with memoization.

My Code Explanation : So I maintained a state (prefix, suffix). Now at any given point only one of it would be set and other would be empty. So wlog assume the current state is (some_prefix, ""). Now I transition to other state by adding some string at the end (i.e suffix) maintaining the palindromic condition. Now since possible values of some_prefix ~ 50 * 20C2 (total strings * total substrings of one string), it's feasible to do so.

Can someone please help me where I might be going wrong? I am not able to wrap my head around it.

»
4 years ago, # |
  Vote: I like it +13 Vote: I do not like it

Run this on the following test and see what happens:

3
ab 1
abcd 2
badc 2
  • »
    »
    4 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Thank you so much! I understand the error, that is while solving for a particular state -> after certain steps it may get the same state and it's stuck in infinite recursion.

    Btw if possible, perhaps can you explain (or share your intuition) what was the fundamental thing that I missed or rather how Dijkstra was useful (It's obvious via the example but just wanted to understand the thought process).

    • »
      »
      »
      4 years ago, # ^ |
        Vote: I like it +13 Vote: I do not like it

      Since you mentioned me, I'll say that I chose dijkstra because it seemed like the shortest path to AC — ok, enough of that — that is, it was the simplest option to write code for. With DP, there's the potential of worrying about cycles in states, as you just saw (with a cycle, there's also no logical choice of which element to compute first, and if you choose wrong you'll get WA). But with dijkstra, all you have to do is create the transition graph (which you would do anyway with DP) and run the algorithm.

      Sure, maybe it creates an extra log factor, but there are such a small number of edges anyway that it doesn't matter.