UVa 12418 - Game of 999

来源:互联网 发布:中国海关进出口数据库 编辑:程序博客网 时间:2024/05/22 04:27

博主本来只想虐虐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判通 , 然后稍稍修改代码就可以啦

0 0
原创粉丝点击