谷歌中国算法比赛解题报告 APAC2017C

Problem A. Monster Path

Problem B. Safe Squares

Problem C. Evaluation

Problem D. Soldiers


终于快写完了,lz也越来越懒。。。

考试的时候lz异想天开用python刷题……结果你懂的……lz的python毕竟也不熟练

这题前三题不说了(好吧我没做),说说第四题


这题很难,如果用标准的博弈思想,算法复杂度是O(n4),大数据托托超时

但是这题可以优化,优化后能达到O(n2)!!!

首先分析一下标准做法,DFS, 哪怕我们记录每一步的结果,我们还是找了太多的无效节点,要知道要判断这个点能不能赢,我们要判断以这个点为起点的所有情况中,有没有一个对方的失败点,而这情况太多了

但是这题可以倒过来找,跟APAC2015有一道addtion很像,倒着找,所有无效的节点都不用管了!!!


首先,A,D都取最大值一定是一个胜点,如果这个点存在卡牌,直接返回胜利

如果不存在,则横着扫描,所有A,其它D,都是负点,因为攻击为D的卡牌必然有,如果我选了A,其它D,那么你可以选任意A,D这张卡

同理,竖着扫,所有 其它A,D都是负点

然后扫到第二行第二列的点,这个点的正上方的列点如果都是负点,并且这个点的正左方的点如果都是负点,这个点就是胜点,然后这个点正右方,正下方就都变成负点,如果这个点上方或左方有胜点,那么它就是负点

对于胜点,查看能不能选到,选到就返回,

否则,继续往后扫,如果扫的点已经处理过,就跳过

这样是O(n3) 但其实我们对每个点,只要查上方一个点和左方一个点就好,具体原因,自己想……

这样就是O(n2)

说的不是很清楚(lz承诺……要是过了E,你懂的……),具体怎么搞,请看代码……

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <math.h>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <hash_map>
#include <hash_set>
#include <unordered_map>
#include <unordered_set>
#include <string.h>
#include <queue>
#include <list>

using namespace std;

#define real long long






//DFS 做法,耗时n4
class CD
{
public:
	CD(){}
	int M, N;

	map<int, set<int>> amap;
	map<int, set<int>> dmap;

	map<int, map<int,int>> winMap;

	int checkWinLose(int a, int d)
	{
		map<int, map<int, int>>::iterator it1;
		map<int, int>::iterator it2;
		it1 = winMap.find(a);
		if (it1 != winMap.end())
		{
			it2 = it1->second.find(d);
			if (it2 != it1->second.end())
			{
				return it2->second;
			}
		}
		return 0;
	}

	void setWinPoint(int a, int d,int sign=1)
	{
		winMap[a][d] = sign;
		map<int, set<int>>::iterator iter, iter2;

		if (*(dmap[d].begin()) < a)
		{
			for (iter = dmap.begin(); iter != dmap.end() && iter->first < d; iter++)
			{
				winMap[a][iter->first] = -sign;
			}
		}
		
		if (*(amap[a].begin()) < d)
		{
			for (iter = amap.begin(); iter != amap.end() && iter->first < a; iter++)
			{
				winMap[iter->first][d] = -sign;
			}
		}
		
	}

	bool DFS(int a, int d)
	{
		map<int, set<int>>::iterator iter, iter2;
		iter = amap.end();
		iter--;
		for (; iter != amap.end()&&iter->first>a;iter--)
		{
			set<int>::iterator siter;
			for (siter = iter->second.begin(); siter != iter->second.end();siter++)
			{
				int na, nd;
				na = iter->first;
				nd = max(*siter, d);
				int k = checkWinLose(na,nd);
				if (k == -1)
				{
					setWinPoint(a, d);
					return true;
				}
				else if (k == 1)
				{
					continue;
				}
				else
				{
					bool r=DFS(na, nd);
					if (r)
					{
						continue;
					}
					else
					{
						setWinPoint(a, d);
						return true;
					}
				}
			}
		}

		//same as a
		iter = dmap.end();
		iter--;
		for (; iter != dmap.end() && iter->first>d; iter--)
		{
			set<int>::iterator siter;
			for (siter = iter->second.begin(); siter != iter->second.end();siter++)
			{
				int na, nd;
				na = max(*siter,a);
				nd = iter->first;
				int k = checkWinLose(na, nd);
				if (k == -1)
				{
					setWinPoint(a, d);
					return true;
				}
				else if (k == 1)
				{
					continue;
				}
				else
				{
					bool r = DFS(na, nd);
					if (r)
					{
						continue;
					}
					else
					{
						setWinPoint(a, d);
						return true;
					}
				}
			}
		}

		setWinPoint(a, d, -1);
		return false;

	}

	string SingleProcess()
	{
		amap[0].insert(0);
		dmap[0].insert(0);
		setWinPoint(amap.rbegin()->first, dmap.rbegin()->first,-1);
		if (DFS(0, 0))
		{
			return "YES";
		}
		return "NO";
	}

	void run()
	{
		FILE* fp = freopen("in.txt", "r", stdin);
		ofstream fout("out.txt");
		int Cases = 0;
		scanf("%d", &Cases);
		for (int time = 0; time < Cases; time++)
		{
			scanf("%d", &N);
			int A, D;
			amap.clear();
			dmap.clear();
			winMap.clear();
			for (int i = 0; i < N; i++)
			{
				scanf("%d %d", &A, &D);
				amap[A].insert(D);
				dmap[D].insert(A);
			}

			fout << "Case #" << (time + 1) << ": " << SingleProcess().c_str() << endl;
			std::cout << time << endl;
		}
		fclose(fp);
		fout.close();
	}
};

//浓缩后倒着推,找到一个就返回,最大耗时n2,但condense比较耗时,关键点,找一个点是不是胜点只要考虑该行该列(再优化到只考虑比它大一的行列),而DFS要找的是比自身大的所有行所有列。如果不condense,向上移动的操作比较麻烦。
class CX1
{
public:
	CX1(){}
	int M, N;
	int MA, MD;
	struct INT2
	{
		int x, y;
		INT2(){ x = y = 0; }
	};
	map<int,int> amap;
	map<int,int> dmap;
	vector<int> cmin;
	vector<int> rmin;
	vector<INT2> solders;
	vector<vector<bool>> matrix;
	vector<vector<int>> winMap;

	string SingleProcess()
	{
		for (int r = MA - 1; r > 0;r--)
		{
			for (int c = MD - 1; c > 0; c--)
			{
				if (winMap[r][c] != 0) continue;

				bool findone = false;
				if(r+1 < MA)
				{
					if (winMap[r+1][c] == -1 && c>rmin[r+1])
					{
						findone = true;
						for (int j = r; j > 0; j--) winMap[j][c] = 1;
					}
				}
				if (!findone&&c+1<MD)
				{
					if (winMap[r][c+1] == -1 && r>cmin[c+1])
					{
						findone = true;
						for (int j = c; j > 0; j--) winMap[r][j] = 1;
					}
				}
				if (!findone)
				{
					winMap[r][c] = -1;
					if (matrix[r][c]) return "YES";
				}
			}
		}
		return "NO";
	}

	void run()
	{
		FILE* fp = freopen("in.txt", "r", stdin);
		ofstream fout("out.txt");
		int Cases = 0;
		scanf("%d", &Cases);
		for (int time = 0; time < Cases; time++)
		{
			scanf("%d", &N);
			amap.clear();
			dmap.clear();
			solders.clear();
			cmin.clear();
			rmin.clear();
			solders.resize(N, INT2());
			winMap.clear();
			for (int i = 0; i < N; i++)
			{
				scanf("%d %d", &solders[i].x, &solders[i].y);
				amap[solders[i].x] = 0;
				dmap[solders[i].y] = 0;
			}
			MA = MD = 1;
			for (map<int, int>::iterator iter = amap.begin(); iter != amap.end(); iter++)
			{
				iter->second = MA++;
			}
			for (map<int, int>::iterator iter = dmap.begin(); iter != dmap.end(); iter++)
			{
				iter->second = MD++;
			}
			matrix.clear();
			matrix.resize(MA, vector<bool>(MD,false));
			winMap.clear();
			winMap.resize(MA, vector<int>(MD, 0));
			cmin.resize(MD, INT_MAX);
			rmin.resize(MA, INT_MAX);
			for (int i = 0; i < solders.size(); i++)
			{
				int t1 = amap[solders[i].x];
				int t2 = dmap[solders[i].y];
				matrix[t1][t2] = true;
				cmin[t2] = min(cmin[t2], t1);
				rmin[t1] = min(rmin[t1], t2);
			}
			fout << "Case #" << (time + 1) << ": " << SingleProcess().c_str() << endl;
			std::cout << time << endl;
		}
		fclose(fp);
		fout.close();
	}
};

//不condense,充分使用map,但利用了题目中10000*10000的数组,否则只能condense
//红黑树做索引,vector做存储matrix,利用10000*10000,效率很高!!!
class CX2
{
public:
	CX2(){}
	int M, N;
	int MA, MD;
	struct INT2
	{
		int x, y;
		INT2(){ x = y = 0; }
	};
	map<int, set<int>> amap;
	map<int, set<int>> dmap;
	//map<int, map<int, int>> winMap;  //红黑树不能处理,速度太慢而且内存占用非常高
	//unordered_map<int, unordered_map<int, int>> winMap;  //hash表的速度依然慢到爆表,而且内存占用非常高,和红黑树差不多
	vector<vector<int>> winMap; //vector可以处理,综合速度比condense慢一些,但不太大,毕竟10000*10000和4000*4000的区别,内存上差很多
	string SingleProcess()
	{
		map<int, set<int>>::iterator aiter, diter,t1,t2;
		set<int>::iterator siter;
		map<int, int> temp;
		for (aiter = amap.end(), aiter--; aiter != amap.end(); aiter--)
		{
			for (diter = dmap.end(), diter--; diter != dmap.end(); diter--)
			{
				if (winMap[aiter->first][diter->first]!=0) continue;//这个winMap必然要填满,所以就这么写了

				bool findone = false;
				t1 = aiter;
				t1++;
				if (t1 != amap.end() && winMap[t1->first][diter->first] == -1 && diter->first>*(t1->second.begin()))
				{
					findone = true;
					for (t1 = aiter; t1 != amap.end(); t1--) winMap[t1->first][diter->first] = 1;
				}
				t1 = diter;
				t1++;
				if (!findone&&t1!=dmap.end()&&winMap[aiter->first][t1->first]==-1&&aiter->first>*(t1->second.begin()))
				{
					
						findone = true;
						for (t1 = diter; t1 != dmap.end(); t1--) winMap[aiter->first][t1->first] = 1;
				}

				if (!findone)
				{
					winMap[aiter->first][diter->first] = -1;
					if (amap[aiter->first].find(diter->first)!=amap[aiter->first].end()) return "YES";
				}
			}
		}
		return "NO";
	}

	void run()
	{
		FILE* fp = freopen("in.txt", "r", stdin);
		ofstream fout("out.txt");
		int Cases = 0;
		scanf("%d", &Cases);
		for (int time = 0; time < Cases; time++)
		{
			scanf("%d", &N);
			amap.clear();
			dmap.clear();
			winMap.clear();
			winMap.resize(10001, vector<int>(10001,0));
			int A, D;
			for (int i = 0; i < N; i++)
			{
				scanf("%d %d", &A,&D);
				amap[A].insert(D);
				dmap[D].insert(A);
			}
			fout << "Case #" << (time + 1) << ": " << SingleProcess().c_str() << endl;
			std::cout << time << endl;
		}
		fclose(fp);
		fout.close();
	}
};


int main()
{
	//CA p;
	//CB p;
	//CC p;
	CX2 p;
	p.run();

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值