275: A comb game problem, a straightforward approach is to use dp with memorization. A state is (a, b, turn, move)
bool check(int a, int b, int turn, int move)
// turn = 0 for Manao, 1 for the opponent, move is at most 1000
// a and b has only (10 choose 2) choices, so total number of states
// is around 100 * 100 * 2 * 1000 = 10^7
base case, or terminal states:
1. move = 1000, then, if turn = 1, return true, else return false, because Manao loses
2. a = b, then if turn = 0, return true, else return false, because Manao wins
Other cases:
1. reverse a
2. a div 10
3. reverse b
4. b div 10
call check(anew, bnew, 1-turn, 1+move)
if any of them is false, that means next player loses, then return true
else all of them are true, then this position is a lose position, return false
The solution is by calling check(A, B, 0, 1)
450:
The problem statement is the same as p1000 in div2, however now N can be as large as 18. Does backtrack still work? Should not be because the return type is long long, and one testcase clearly have ans more than 10^9, so an algorithm computes ans by increment of 1 will definitely TLE. How to count faster?
// this is like TSP problem, so think about O(2^n * n) algorithm
// given a prefix with last, we want to extend the prefix by node = next
// In this problem we need to know whether prefix intersects (last, next)
// so it appears we need to know the actual prefix, not only the mask of all
// nodes in prefix, but then the algorithm would be n factorial which is too slow!
//
// Now comes the observation: we only need to know the subset of nodes in prefix,
// to determine intersection, their order does not matter. Let S be the set of nodes
// in prefix, then S contains a pair (a, b) that intersects (last, next) if and only
// if S contains nodes both inside and outside the range of (a, b), regardless of
// how nodes in S are ordered. Because we only need to know whether S crosses
// (last, next) or not, this check is enough.
#include <cassert>
#include <algorithm>
#include <vector>
using namespace std;
long long dp[1<<18][20]; // careful, dp[1<<20][20] for long long will mem limit exceeded, SIGKILL
class PolygonTraversal
{
public:
bool inrange(int a, int b, int val)
{
assert(a < b && a != val && b != val);
return a < val && val < b;
}
bool cross(int a, int b, const vector<int> &vec)
{
bool inside, outside;
inside = outside = false;
for (int i = 0; i < vec.size(); ++i) {
int k = vec[i];
if (inrange(a, b, k)) inside = true;
else outside = true;
}
return inside && outside;
}
long long count(int N, vector <int> points)
{
for (int i = 0; i < points.size(); ++i) points[i]--;
int start = 0;
for (int i = 0; i < points.size(); ++i) start |= 1 << points[i];
dp[start][points.back()] = 1;
for (int mask = 0; mask < (1 << N); ++mask) if ((mask & start) == start) { // mask contains start
for (int last = 0; last < N; ++last) if (mask & 1 << last) { // mask has last
vector<int> prefix;
for (int i = 0; i < N; ++i) if ((mask & 1 << i) && i != last)
prefix.push_back(i);
for (int next = 0; next < N; ++next) if (!(mask & 1 << next)) { // next is not in mask
int a, b;
a = min(last, next); b = max(last, next);
if (cross(a, b, prefix)) {
dp[mask | 1<<next][next] += dp[mask][last];
}
}
}
}
long long ans = 0;
for (int last = 0; last < N; ++last) if (last != points[0]) {
vector<int> vec;
for (int i = 0; i < N; ++i) if (i != points[0] && i != last) vec.push_back(i);
int a, b;
a = min(points[0], last); b = max(points[0], last);
if (cross(a, b, vec))
ans += dp[(1<<N) - 1][last];
}
return ans;
} // end of method
}; // end of class
1000:Not read yet.
Conclusion: Had I been competing in div1, I should have p250, but no handle on 450 and 1000.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment