UVa 12418 - Game of 999

博主本来只想虐虐NOIP级别的暴力的.........


1. 本题状态很简单 , 哪些边用了 , 现在各点在哪里 , 把vector状态压缩成一个long long(代码中的ull 是 long long).

2. 转移的思路不复杂 , 看看哪些点能够从某一房间转移到另外一些房间    (不知道不预处理的版本能不能过 , 但是博主进行了一次预处理)


写写吧 , 虽然肯定超时 , 但是正解离你将要写的代码修改的不过3,4行 , 写完后尝试以下数据


就这样写 ,   样例跑的很快 , 但是有一组数据肯定过不了

10  10
1  2  0
2  3  0
3  4  0
4  5  0
5  6  0
6  7  0
7  8  0
8  9  0
9 10 0
10 1 0

(博主想了各种优化方法 , 但是这个复杂度需要的优化不是常数级别的 , 也就是题目中的hint 所说的)


观察发现 , 其实状态是很有限的 , 因为很多路都只能经过一次 , 有用的状态尤其有限  (比如上述数据中从1到10然后回到1肯定是无用状态)

那该怎么处理呢?  请思考些时间 , 提示: 无用的状态是什么造成的


如果有思路了就动手吧  , 下面是代码 余下的所有分析在代码后


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <algorithm>
#include <set>
#include <queue>
#include <list>

using namespace std;
typedef long long ull;
const int maxn = 12;
const int maxm = 12;

int n , m;

int u[maxm] , v[maxm] , w[maxm];
vector<int> g[maxn];
int allow[maxm][1<<maxn];
ull q[10000000];
set<ull> dic;

ull encode(vector<int>& s)
{
	ull res = 0;
	for(int i=1;i<s.size();i++) res = res*11+s[i];
	res = res*1200+s[0];
	return res;
}

void decode(vector<int>& s , ull v)
{
	s[0] = v%1200; v/=1200;
	for(int i=(int)s.size()-1;i>=1;i--) s[i] = v%11 , v/=11;
}

int reachable[maxn][maxn];

int main(int argc, char *argv[]) {
	
	while(cin>>n>>m)
	{
		dic.clear();
		memset(allow, 0, sizeof(allow));
		for(int i=1;i<=n;i++) g[i].clear(); 
		memset(reachable, 0, sizeof(reachable)); 
		for(int i=1;i<=m;i++) { cin>>u[i]>>v[i]>>w[i]; if(w[i]) g[u[i]].push_back(i); else reachable[u[i]][v[i]] = 1; }
		
		for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) reachable[i][j] |= ( reachable[i][k] && reachable[k][j] );
		
		for(int i=1;i<(1<<9);i++) if(__builtin_popcount(i)>=3 && __builtin_popcount(i)<=5)
		{
			int num = 0;
			for(int j=1;j<=9;j++) if(i&(1<<(j-1))) num+= j;
			while(num>=10) num = num/10+num%10;
			
			for(int j=1;j<=m;j++) if(w[j]==num) allow[j][i] = 1;
		}
		for(int j=1;j<=m;j++) if(w[j]==0) for(int i=1;i<512;i++) allow[j][i] = 1;
		int book[maxn];
		vector<int> beg;
		set<int> ans; int Max = 0;
		
		beg.push_back((1<<m)-1);
		for(int i=1;i<=9;i++) beg.push_back(1);
		
		int l = 0 , r = 0;
		q[r++] = encode(beg);
		dic.insert(encode(beg));
		
		while(l!=r)
		{
			vector<int> now = beg , ne;
			decode(now, q[l++]);
			
			memset(book, 0, sizeof(book));
			for(int i=1;i<now.size();i++) book[now[i]] |= (1<<(i-1));
			for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(reachable[i][j]) book[j] |= book[i]; 
			
			if(book[n] && __builtin_popcount(book[n])>Max) { Max = __builtin_popcount(book[n]); ans.clear(); ans.insert(book[n]); }
			else if(book[n] && __builtin_popcount(book[n])==Max) if(ans.count(book[n])==0) ans.insert(book[n]);
			
			for(int i=1;i<=n;i++) for(int j=0;j<g[i].size();j++)
			{
				int way = g[i][j] , to = v[way];
				if(!(now[0]&(1<<(way-1)))) continue;
				
				for(int s = book[i];s>0;s = (s-1)&book[i]) if(allow[way][s])
				{
					ne = now;
					for(int l=1;l<=9;l++) if(s& (1<<(l-1))) ne[l] = to;
					if(w[way]) ne[0]^= 1<<(way-1);
					ull hashNow = encode(ne);
					if(dic.count(hashNow)) continue;
					dic.insert(hashNow);
					q[r++] = encode(ne);
				}
			}
		}
		
		vector<string> output;
		for(set<int>::iterator i=ans.begin();i!=ans.end();++i) 
		{
			int now = *i;  string ins;
			for(int j=1;j<=9;j++) if(now&(1<<(j-1))) ins = ins+((char)(j+'0'));
			output.push_back(ins); 
		} 
		sort(output.begin(), output.end()); 
		cout<<Max;
		for(int i=0;i<output.size();i++) cout<<" "<<output[i]; 
		cout<<endl; 
	}
	
	return 0;
}



接着上面的说 , 产生无用状态的就是权值为0的边 , 我们想办法把它去掉 , 也就是砍掉在权值为0的边上走来走去的时间 , 我们发现其实我们不需要知道每个点到底具体在哪里 , 我们只要能知道这个点可以在哪里就行了 , 一个点可以在的地方不仅仅是它现在在的地方 , 还可以是与这个点通过0权边联通的任意点(因为每次状态转移我们都进行一次操作 , 所以不需要考虑一个点被多次转移)


剩下的就是floyd判通 , 然后稍稍修改代码就可以啦

你可能感兴趣的:(压缩,uva,暴搜)