TopCoder SRM 685 Div1 450 FoxAirline2

这题可以用一个dfs
对于每条边,我们尝试将它染成某一种颜色。
有一个显而易见的小优化:如果一方的这两个点已经联通,就不用连这条边。

其实这样复杂度就对了。。
于是我又从网上粘了一个关于复杂度的解释(原文是日语,这是Google翻译的结果,稍微润色了一下,原文

由于N<=10,如果两种颜色都有9条边,则可以形成一颗生成树。
出于这个原因,“如果你能以两种颜色重新连接,同时尝试两者”的情况最多只发生9次,所以DFS分支最多有2^9。

另外,我还在cf上看到了鏼大神的随机大法,感觉很神,原文

I randomly generate a permutation of m edges and greedily (like Kruskal) find a spanning tree, then check if the remaining edges are valid. I do this for 300000 times and it passed ST. Maybe it’s hard to generate testcases against this approach.

另一个大神提供了正确性证明

If there is a solution with 18 edges, then any random permutation where the 9 edges belonging to the first partition are before the 9 edges of the second partition works. The probability of getting permutation like this is at least 9 ! 9 ! / 18 ! {9!9!}/{18!} 9!9!/18!. Therefore the probability of failing if you do 300000 iterations is at most ( 1 − 9 ! 9 ! / 18 ! ) 300000 ≈ 0.00209 (1-{9!9!}/{18!})^{300000} \approx 0.00209 (19!9!/18!)3000000.00209.

代码可能有点奇葩,我是把并查集里所有点的father压成一个数的…因为总感觉传参穿个数组会很奇怪(其实也不奇怪),每次改了dfs完再改回来会很麻烦(其实只要不路径压缩就不麻烦,这题也没必要路径压缩)。。所以这个代码可能比较丑O—O

#include <bits/stdc++.h>
#define ll long long
#define fr(i,x,y) for(int i=x;i<=y;i++)
#define rf(i,x,y) for(int i=x;i>=y;i--)
using namespace std;
const int M=51;
int n,m,u[M],v[M];

template<class T> void checkmin(T &a,const T &b) { if (b<a) a=b; } 
template<class T> void checkmax(T &a,const T &b) { if (b>a) a=b; }

class FoxAirline2 {
public:
    string isPossible( int n, vector <int> a, vector <int> b ) ;
};

int gf(int x,int f[]){
	return f[x]==x?x:f[x]=gf(f[x],f);
}

inline void unio(int x,int y,int f[]){
	f[gf(x,f)]=gf(y,f);
}

int dfs(int k,ll st1,ll st2){
	int f1[10],f2[10],cnt1=0,cnt2=0,d1[10],d2[10];
	ll ost1=st1,ost2=st2;
	rf(i,n-1,0){
		f1[i]=st1%10;st1/=10;
		f2[i]=st2%10;st2/=10;
		if (f1[i]==i) cnt1++;
		if (f2[i]==i) cnt2++;
	}
	if (cnt1==1&&cnt2==1) return 1;
	if (k>m) return 0;
	//cout<<k<<' '<<st1<<' '<<st2<<endl;
	int x=u[k],y=v[k];
	if (gf(x,f1)==gf(y,f1)&&gf(x,f2)==gf(y,f2)) return dfs(k+1,ost1,ost2);
	//int flag;
	if (gf(x,f2)!=gf(y,f2)){
		//memcpy(d1,f1,sizeof d1);
		memcpy(d2,f2,sizeof d2);
		unio(x,y,d2);
		fr(i,0,n-1) st2=st2*10+d2[i];
		if (dfs(k+1,ost1,st2)) return 1;
	}
	if (gf(x,f1)!=gf(y,f1)){
		memcpy(d1,f1,sizeof d1);
		unio(x,y,d1);
		fr(i,0,n-1) st1=st1*10+d1[i];
		if (dfs(k+1,st1,ost2)) return 1;
	}
	return 0;
}

string FoxAirline2::isPossible(int N, vector <int> a, vector <int> b) {
    m=a.size();n=N;
    fr(i,1,m) u[i]=a[i-1],v[i]=b[i-1];
    ll st=0;
    fr(i,0,n-1) st=st*10+i;
    if (dfs(1,st,st)) return "Possible";
     else return "Impossible";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值