codeforce Gym 100203I I WIN (网络流)

把‘I‘拆成容量为1一条边,一个入点一个出点,入点和相邻的‘W‘连一条容量为1的边,出点和相邻的‘N‘连一条容量为1,所有的‘W‘和源点连一条容量为1边,所有的‘N‘和汇点连一条容量为1的边,表示只能用一次。一发网络流就过了。

写了4000B+的贪心,然并卵

#include<bits/stdc++.h>
using namespace std;

const int INF = 0x3fffffff;
const int maxn = 2142;
#define PB push_back
struct Edge
{
    int from,to,cap,flow;
};

vector<Edge> edges;
vector<int> G[maxn];
int S ,T ;

void AddEdge(int from,int to,int cap)
{
    edges.PB(Edge{from,to,cap,0});
    edges.PB(Edge{to,from,0,0});
    int m = edges.size();
    G[from].PB(m-2);
    G[to].PB(m-1);
}

bool vis[maxn];
int d[maxn],cur[maxn];

bool bfs()
{
    memset(vis,0,sizeof(vis));
    queue<int> q;
    q.push(S);
    d[S] = 0;
    vis[S] = true;
    while(q.size()){
        int u = q.front(); q.pop();
        for(int i = 0; i < G[u].size(); i++){
            Edge &e = edges[G[u][i]];
            if(!vis[e.to] && e.cap > e.flow){
                vis[e.to] = true;
                d[e.to] = d[u] + 1;
                q.push(e.to);
            }
        }
    }
    return vis[T];
}

int dfs(int u,int a)
{
    if(u == T || a == 0) return a;
    int flow = 0, f;
    for(int &i = cur[u]; i < G[u].size(); i++){
        Edge &e = edges[G[u][i]];
        if(d[u] + 1 == d[e.to] && (f = dfs(e.to,min(a,e.cap-e.flow))) >0){
            e.flow += f;
            edges[G[u][i]^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}

int MaxFlow()
{
    int flow = 0;
    while(bfs()){
        memset(cur,0,sizeof(cur));
        flow += dfs(S,INF);
    }
    return flow;
}

vector<pii> vec;

const int N = 24;
char s[N][N];
int id[N][N];

int dx[] = {1,0,-1,0};
int dy[] = {0,-1,0,1};

int main()
{
    //freopen("in.txt","r",stdin);
    int n,m; scanf("%d%d",&n,&m);
    int Icnt = 0, Wcnt = 0, Ncnt = 0;
    for(int i = 1; i <= n; i++){
        scanf("%s",s[i]+1);
        for(int j = 1; j <= m; j++){
            if(s[i][j] == ‘I‘){
                vec.PB(MP(i,j));
                id[i][j] = Icnt++;
            }
            if(s[i][j] == ‘W‘){
                id[i][j] = Wcnt++;
            }
            if(s[i][j] == ‘N‘){
                id[i][j] = Ncnt++;
            }
        }
    }
    int MW = Icnt*2  + 5;
    int MN = MW + Wcnt + 5;
    S = MN +Ncnt + 4; T = MN +Ncnt + 8;
    for(int i = 0; i < vec.size(); i++){
        int x = vec[i].fi, y = vec[i].se;
        int in = 2*i+1, out = 2*i+2;
        AddEdge(in,out,1);
        for(int k = 0; k < 4; k++){
            int nx = x+dx[k], ny = y+dy[k];
            if(s[nx][ny] == ‘W‘){
                int u = id[nx][ny]+MW;
                AddEdge(u,in,1);
            }
            if(s[nx][ny] == ‘N‘){
                int v = id[nx][ny]+MN;
                AddEdge(out,v,1);
            }
        }
    }
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++){
            if(s[i][j] == ‘W‘){
                int v = id[i][j]+MW;
                AddEdge(S,v,1);
            }
            if(s[i][j] == ‘N‘){
                int u = id[i][j]+MN;
                AddEdge(u,T,1);
            }
        }
    int ans = MaxFlow();
    printf("%d",ans);
    return 0;
}
时间: 2024-10-13 04:50:15

codeforce Gym 100203I I WIN (网络流)的相关文章

Gym - 100203I I WIN 网络流

Gym - 100203I  I WIN 题意:一个n*m的矩阵包含W,I,N三种字符,问相邻的字符最多能组成不重叠的WIN. 思路:比赛的时候没有发现是网络流,,居然一度以为是二分图匹配,,写了一下没过就没改了,,知道了是网络流就好办了.设一个起点一个终点,起点和每个W之间连一条边,N和终点间连一条边,W和I之间连一条边,I和N之间连一条边,不过这里为了避免重复使用同一个I,应改成W到I连一条边,I连一条边到I',再从I'连一条边到N就可以保证最优解了.求一遍最大流即可. 1 #include

Codeforces Gym 100203I I WIN 最大流

原题链接:http://codeforces.com/gym/100203/attachments/download/1702/statements.pdf 题解 首先寻找每个I,然后枚举形状,如果匹配的话,就将源点连一条边到当前匹配的W,再从W连条边到I,I需要拆点,然后将拆点后面的那个点连接到N,从N连接到汇点.所有边的容量都是1.需要注意避免产生重边.然后最大流就是答案. 代码 #include<iostream> #include<stack> #include<ve

Codeforces Gym 100203I I - I WIN 网络流最大流

I - I WINTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=87954#problem/I Description Given an n × m rectangular tile with each square marked with one of the letters W, I, and N, find the maximal numb

codeforce gym 100307H Hack Protection

原题地址:http://codeforces.com/gym/100307/problem/H 题意: 给定一个序列,求序列的子区间中,满足子区间XOR值等于AND值得子区间个数. 题解: 一直以为NEERC这种有名的比赛应该题解到处都是,太天真了…… 首先考虑区间的AND值. 对于固定起点的区间,因为 & 的性质,AND值必然单调递减,并且,根据AND值中1的个数划分后面的区间,必然最多只能划分成32段,对于某一段中的任意位置,它和固定的起点所形成的子区间的AND值是一样的. 再来考虑区间的X

codeforce Gym 100418K Cards (概率,数学)

题意:麦田的故事,n张牌,取x张牌,记住前x张牌最大的值m,继续往后取,遇到第一张比m大的牌就停下来.求一个x使得最后的牌在整副牌里是最大的期望最大. 假设最大的牌是A,A在各种位置出现的概率就是相等的,在A固定的情况下,在它前面的牌中最大的牌B,出现在各个位置的概率也是相等的.所以就是要求一个X,使得 下面这个矩形框中的概率和最大. 样例,n=5 #include<bits/stdc++.h> int main() { int n;scanf("%d",&n);

codeforce Gym 100685E Epic Fail of a Genie(贪心)

题意:给出一堆元素,求一个子集,使子集的乘积最大,如有多个,应该使子集元素个数尽量小. 题解:贪心,如果有乘积大于1的正数,那么是一定要选的,注意负数也可能凑出大于1的正数,那么将绝对值大于1的负数两两配对,如果还剩下一个,那么在判断一下,那个负数和比它小的最小负数的乘积是否大于1,如果是那么就选这两个.把所有可能凑成大于1的数选完以后,剩下的数一定比1小,那么就不选. 如果无法凑出大于1的数,那么再分类讨论一下. 挺容易写错... #include<bits/stdc++.h> using

codeforce gym 100548H The Problem to Make You Happy

题意: Alice和Bob在一个有向图上玩游戏,每个人各自操作一个棋子,如果两个棋子走到一个点上,判定Bob输:如果轮到任何一方走时,无法移动棋子,判定该方输 现在Bob先走,要求判断胜负 题解 模型上看是SG问题,但是通常的SG做法需要DP,但是考虑这不是DAG模型,普通的记忆化搜索写法会RE 正解的DP做法:dp[i][j][k]:i,j是Bob,Alice的位置,k是目前轮到谁走了. 开始将所有显然的Bob输的情况加入队列中,不断拓展,找到所有的Bob输的情况. 转移类似SG #inclu

codeforce Gym 101102A Coins (01背包变形)

01背包变形,注意dp过程的时候就需要取膜,否则会出错. 代码如下: #include<iostream> #include<cstdio> #include<cstring> using namespace std; #define MAXW 15005 #define N 155 #define LL long long #define MOD 1000000007 int w1[N],w2[N]; LL dp1[MAXW],dp2[MAXW]; int main(

codeforce Gym 100342J Triatrip (bitset)

傻逼题,但是为什么别人的O(n^3)不会T?只是因为用了bitset优化... 附上一张bitset基本操作的表 #include<bits/stdc++.h> using namespace std; const int maxn = 1500+2; char g[maxn][maxn]; bitset<maxn> b1[maxn],b2[maxn],res; #define local int main() { #ifdef local freopen("triatr