zoukankan      html  css  js  c++  java
  • HDU 3446 daizhenyang's chess

    http://acm.hdu.edu.cn/showproblem.php?pid=3446

    题意

    一个棋盘,有个KING,有一些能走的点,每次只能走到没走过的地方,没路可走的输,求先手是否必胜。

    题解

    一般图最大匹配,判断KING是否一定在最大匹配中,在的话一定先手必胜.

    判断KING一定在最大匹配的方法就是加入KING跑最大匹配,然后不加入KING再跑一次最大匹配看最大匹配数量是否改变.

    在最大匹配一定先手必胜原因:

    如果KING在最大匹配,那么先手每次都走匹配边,后手就只能走非匹配边,而后手走到的点一定是匹配点

    此刻只需要让先手一直走匹配边,就可以必胜了.

    非匹配边走到的点一定是匹配点的原因:

    假如这个点没有匹配,那么可以将之前所有经过的路径匹配边变成非匹配边,非匹配边变成匹配边,那么此时的匹配个数并没有发生改变

    而KING变成了非匹配点,不符合KING在最大匹配的前提

    KING一定变成了非匹配点的原因:

    因为原匹配是最大匹配,而翻转边后匹配个数不变,如果KING变成了匹配点就增加了一个匹配,不符合原匹配是最大匹配的前提

    不在最大匹配一定先手必败的原因:

    如果KING不在最大匹配,那么一定存在一种情况满足KING是非匹配点,此时先手无论怎么走走到的都是匹配点,那对手就每次都可以走匹配边,就可以必胜了.

    非匹配点走到的点一定是匹配点的原因:

    假如这个

    我被教育了

    假如非匹配点走到的点是非匹配点,不就能匹配了吗

    #include<bits/stdc++.h>
    #define id(x,y) ((x-1)*m+y)
    using namespace std;
    const int N=20;
    inline int rd(register int x=0,register char ch=getchar(),register int f=0){
    	for(;!isdigit(ch);ch=getchar()) f=ch=='-';
    	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-48;
    	return f?-x:x;
    }
    int n,m;
    int match[N*N],vis[N*N];
    int dx[]={-1,-1,-1,1,1,1,0,0,2,-2,2,-2,2,-2,2,-2,1,-1,-1,1};
    int dy[]={-1,1,0,0,1,-1,-1,1,2,2,-2,-2,1,-1,-1,1,2,-2,2,-2};
    char s[N][N];
    vector<int> e[N*N];
    
    bool check(int x,int y){
    	return (x<=0||x>n||y<=0||y>m||s[x][y]!='.')?0:1;
    }
    void lnk(int x,int y){
    	e[x].push_back(y),e[y].push_back(x);
    }
    int dfs(int x){
    	vis[x]=1;
    	random_shuffle(e[x].begin(),e[x].end());
    	for(auto to:e[x]) if(!match[to]) return vis[to]=1,match[to]=x,match[x]=to,1;
    	for(auto to:e[x]){
    		int y=match[to];
    		if(vis[y]) continue;
    		match[x]=to,match[to]=x,match[y]=0;
    		if(dfs(y)) return 1;
    		match[x]=0,match[to]=y,match[y]=to;
    	}
    	return 0;
    }
    int main(){
    	srand((unsigned)time(0));
    	for(int T=rd(),o=1;o<=T;++o){
    		n=rd();m=rd();
    		for(int i=1;i<=n;++i) scanf("%s",s[i]+1);
    		for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) e[id(i,j)].clear();
    		for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(s[i][j]=='.') for(int k=0;k<20;++k) if(check(i+dx[k],j+dy[k])) lnk(id(i,j),id(i+dx[k],j+dy[k]));
    		int ans=0,tim=0;
    		memset(match,0,sizeof match);
    		while(++tim<3) for(int i=1;i<=n*m;++i) if(!match[i]) memset(vis,0,sizeof vis),ans+=dfs(i);
    		int nans=0,ntim=0;
    		memset(match,0,sizeof match);
    		for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(s[i][j]=='K') for(int k=0;k<20;++k) if(check(i+dx[k],j+dy[k])) lnk(id(i,j),id(i+dx[k],j+dy[k]));
    		while(++ntim<3) for(int i=1;i<=n*m;++i) if(!match[i]) memset(vis,0,sizeof vis),nans+=dfs(i);
    		printf("Case #%d: ",o);
    		puts(ans==nans?"daizhenyang lose":"daizhenyang win");
    	}
    }
    
  • 相关阅读:
    Android Studio 配置快速生成模板代码
    Android开发 AndroidViewModel详解
    adb命令 logcat日志抓取
    Android开发 自定义View_白色圆型涟漪动画View
    Java Queue队列
    Java 几种队列区别的简单说明
    Android开发 retrofit下载与上传
    Android开发 retrofit入门讲解 (RxJava模式)
    Java学习 时间类 Period类与Duration类 / LocalDate类与Instant类 用法详解
    Java 学习 时间格式化(SimpleDateFormat)与历法类(Calendar)用法详解
  • 原文地址:https://www.cnblogs.com/hzoi2018-xuefeng/p/13087536.html
Copyright © 2011-2022 走看看