一个N行,M列的棋盘。棋盘每个格子都是边长为1的正方形。
现在要在棋盘上放一些1×2大小的骨牌。骨牌的边线与格子重合(必须占2个格子),任意两个骨牌不能重叠。
但是棋盘上的一些格子已经被占用,请问最多可以放多少个骨牌。
#include
#include
#include
using namespace std;
const int ms = 105 * 105;
int n, m;
vector<int> G[ms];
int match[ms];
bool visit[ms];
bool dfs(int x)
{
int len = G[x].size();
for (int i = 0; i < len; ++i)
{
int to = G[x][i];
if (!visit[to])
{
visit[to] = true;
if (!match[to] || dfs(match[to]))
{
match[to] = x; return true;
}
}
}
return false;
}
bool p[ms];
inline int to1(int a, int b)
{
return (a - 1)*m + b;
}
int MaxMatch()
{
int ans = 0;
memset(match, 0, sizeof(match));
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
if ((i + j) & 1 || p[to1(i, j)])
continue;
memset(visit, false, sizeof(visit));
if (dfs(to1(i, j))) ans++;
}
}
return ans;
}
int main()
{
int k;
scanf("%d%d%d", &n, &m, &k);
int a, b;
for (int i = 0; i < k; ++i)
{
scanf("%d%d", &a, &b);
p[to1(a, b)] = true;
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
if (!((i + j) & 1) && !p[to1(i, j)])
{
if (i > 1 && !p[to1(i - 1, j)])
G[to1(i, j)].push_back(to1(i - 1, j));
if (j > 1 && !p[to1(i, j - 1)])
G[to1(i, j)].push_back(to1(i, j - 1));
if (i < n && !p[to1(i + 1, j)])
G[to1(i, j)].push_back(to1(i + 1, j));
if (j < m && !p[to1(i, j + 1)])
G[to1(i, j)].push_back(to1(i, j + 1));
}
}
}
printf("%d\n", MaxMatch());
}
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
#include
#include
#include
#include
using namespace std;
const int maxn = 1e4+10;
const int inf = 0x3f3f3f3f;
int n,m,s,t,q,tol,head[maxn],dep[maxn],x[1007][1007];
struct Edge
{
int v,w,nxt;
}E[maxn];
void add_edge(int u,int v,int w)
{
E[tol] = Edge{v,w,head[u]};
head[u] = tol++;
}
void insert(int u, int v, int c)
{
add_edge(u, v, c);
add_edge(v, u, 0);
}
bool Bfs()
{
memset(dep,0, sizeof(dep));
queue<int>q;
while(!q.empty())
q.pop();
q.push(s);
dep[s] = 1;
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i = head[u];i != -1;i = E[i].nxt)
{
if(E[i].w && !dep[E[i].v])
{
dep[E[i].v] = dep[u] + 1;
q.push(E[i].v);
if(E[i].v == t)
return true;
}
}
}
return false;
}
int Dfs(int u,int f)
{
if(u == t)
return f;
int used = 0,d = 0;
for(int i = head[u];i != -1;i = E[i].nxt)
{
if(dep[u] == dep[E[i].v] - 1 && E[i].w)
{
if((d = Dfs(E[i].v,min(f - used,E[i].w))))
{
used += d;
E[i].w -= d;
E[i^1].w += d;
}
}
}
if(!used)
dep[u] = 0;
return used;
}
int Dinic()
{
int max_flow = 0,d;
while(Bfs())
{
while((d = Dfs(s,inf)))
max_flow += d;
}
return max_flow;
}
int main()
{
memset(head,-1,sizeof(head));
memset(x,0,sizeof(x));
scanf("%d%d%d",&n,&m,&q);
int i,j,sum=0,mm=0;
t=n*m+1;
while(q--)
{
int a,b;
scanf("%d%d",&a,&b);
x[a][b]=-1;
}
for(i=1; i<=n; i++)
{
for(j=1; j<=m; j++)
{
mm++;
if(x[i][j]==-1) continue;
if((i+j)%2)
{
insert(0,mm,1);//第一列数与源点相连
if(i>1&&x[i-1][j]!=-1) insert(mm,mm-n,inf);//连上其上面的数
if(i<n&&x[i+1][j]!=-1) insert(mm,mm+n,inf);//连上其下面的数
if(j>1&&x[i][j-1]!=-1) insert(mm,mm-1,inf);//连上其左面的数
if(j<m&&x[i][j+1]!=-1) insert(mm,mm+1,inf);//连上其右面的数
}
else
{
insert(mm,t,1);//第二列数与汇点相连
}
}
}
printf("%d",Dinic());
return 0;
}
一个无向图,N个点编号1~N。M条边,每条边有一个权值c。
问对于每条边,最少删除多少条边后,可以使得存在一个最小生成树包含这条边。
#include
#include
#include
#include
#include
using namespace std;
const int maxv = 100 + 5;
const int maxm = 550;
const int inf = 0x3f3f3f3f;
struct Edge
{
int v, e, f;
};
Edge e[maxm * 2];
int head[maxv * 2], depth[maxv * 2];
int s, t, cnt = 2;
queue<int>q;
inline void addEdge(int from, int to, int val)
{
e[cnt] = { to,head[from],val };
head[from] = cnt++;
e[cnt] = { from,head[to],val };
head[to] = cnt++;
}
bool bfs()
{
memset(depth, 0, sizeof(depth));
while (!q.empty()) q.pop();
q.push(s);
depth[s] = 1;
while (!q.empty())
{
int cur = q.front();
q.pop();
for (int i = head[cur]; i; i = e[i].e)
{
if (!depth[e[i].v] && e[i].f)
{
depth[e[i].v] = depth[cur] + 1;
q.push(e[i].v);
if (e[i].v == t)
return true;
}
}
}
return false;
}
int dfs(int x, int flow)
{
if (x == t) return flow;
int rest = flow;
for (int i = head[x]; i && rest; i = e[i].e)
{
if (depth[x] + 1 == depth[e[i].v] && e[i].f)
{
int f = dfs(e[i].v, min(rest, e[i].f));
if (!f) depth[e[i].v] = 0;
e[i].f -= f;
e[i ^ 1].f += f;
rest -= f;
}
}
return flow - rest;
}
int dinic()
{
int flow = 0;
while (bfs())
{
flow += dfs(s, inf);
}
return flow;
}
Edge ee[maxm];
int main()
{
int n, m, a, b, c;
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n >> m;
for (int i = 0; i < m; ++i)
{
cin >> a >> b >> c;
ee[i] = { a,b,c };
}
for (int i = 0; i < m; ++i)
{
cnt = 2;
memset(head, 0, sizeof(head));
for (int j = 0; j < m; ++j)
{
if (ee[j].f < ee[i].f)
addEdge(ee[j].v, ee[j].e, 1);
}
s = ee[i].v; t = ee[i].e;
cout << dinic() << " ";
}
return 0;
}
cin和cout关闭和stdio得同流步
iostream::sync_with_stdio(false);
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
inline void write(int x)
{
char F[200];
int tmp=x>0?x:-x ;
if(x<0)putchar('-') ;
int cnt=0 ;
while(tmp>0)
{
F[cnt++]=tmp%10+'0';
tmp/=10;
}
while(cnt>0)putchar(F[--cnt]) ;
}
现有一数字序列,从中取出一些数字元素,就可以组成一个等差数列,我们想知道这个等差数列最多能有多少个元素,原序列每个元素最多只能取一次。
#include
#include
#include
#define MaxSize 10005
using namespace std;
int n, ans;
int A[MaxSize];
short int dp[MaxSize][MaxSize];
int main()
{
while(~scanf("%d", &n))
{
for (int i = 0; i < n; ++i)
scanf("%d", &A[i]);
sort(A, A+n);
for (int i = 0; i < n; ++i)
for (int j = i+1; j < n; ++j)
dp[i][j] = 2;
ans = 2;
for (int j = n-2; j > 0; --j) {
int i = j-1, k = j+1;
while(i>=0 && k<n)
{
if(A[i]+A[k] < 2*A[j])
k++;
else if(A[i]+A[k] > 2*A[j])
i--;
else
{
dp[i][j] = dp[j][k] + 1;
if(dp[i][j] > ans)
ans = dp[i][j];
i--, k++;
}
}
}
printf("%d\n", ans);
}
}
从1~n中选取任意多(大于0)个数字,输出所有可能的选择方案一行内的数必须升序排列,相邻两个数用恰好1个空格隔开。方案按照字典序由小到大输出。
#include
#include
bool vis[11];
int answer[11];
int n;
void dfs(int k,int step)
{
answer[step]=k;
vis[k]=1;
for(int i=1;i<=step;i++)
printf("%d ",answer[i]);
printf("\n");
for(int i=k;i<=n;i++)
{
if(!vis[i])
{
vis[i]=1;
dfs(i,step+1);
vis[i]=0;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
dfs(i,1);
}
给定n,m,输出从 1∼n 中选择 m个数的所有排列。
要求按照字典序输出。
//图的去排列
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define maxn 10005
int n,m;
int ans[10],f[10],vis[10];
void dfs(int k)
{
if(k==m+1)
{
for(int i=1;i<=m;i++)
printf("%d ",ans[i]);
printf("\n");
}
else
{
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
vis[i]=1;
ans[k]=f[i];
dfs(k+1);
vis[i]=0;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
f[i]=i;
dfs(1);
}
现在给你n个物种和m条能量流动关系,求其中的食物链条数。
物种的名称为从1到n的编号。
m条能量流动关系形如a b 表示能量从物种a 流向物种b。注意单独的一种孤立生物不算一条食物链。
//DFS
#include
#include
#include
#include
#include
#include
typedef long long ll;
using namespace std;
int n,m;
#define maxn 100010
struct node
{
int to;
int next; //next[i]表示与第i条边同起点的上一条边的储存位置
}edge[maxn*2];
int cnt;
int head[maxn];
int out[maxn],in[maxn];
int vis[maxn];
long long sum=0;
void add(int u,int v)
{
edge[++cnt].to=v; //edge[i]表示第i条边的终点
edge[cnt].next=head[u]; //head[i]表示以i为起点的最后一条边的储存位置
head[u]=cnt;
}
int dfs(int start)
{
int tot=0;
if(out[start]==0&&in[start])
return vis[start]=1;
if(vis[start]) return vis[start];
for(int i=head[start];i!=0;i=edge[i].next)
{
tot+=dfs(edge[i].to);
}
vis[start]=tot;
return tot;
}
int main()
{
memset(head,0,sizeof(head));
cin>>n>>m;
int a,b,c;
while(m--)
{
scanf("%d%d",&a,&b);
add(a,b);//读入表示的是a->b
out[a]++;in[b]++;
}
for(int i=1;i<=n;i++)
{
if(in[i]==0&&out[i])
{
sum+=dfs(i);
}
}
printf("%d",sum);
return 0;
}