洛谷P2774 方格取數問題 BZOJ 1143祭祀river【二分圖最大獨立集】
講解前首先引入兩個概念
二分圖最小點覆蓋集
定義:
在二分圖中求出一個最小點集
使得圖中任意一條邊至少有一個端點在點集內
解法:
對二分圖進行最大匹配
最大匹配數就是二分圖的最小點覆蓋集包含的點數
***************************************
二分圖最大獨立集
定義:
對於一張無向圖
求出一個點數最大的點集
使得點集中任意兩點沒有邊相連
這是圖的最大獨立集
而二分圖的最大獨立集就是字面意思
解法:
對於一個有n個結點的二分圖
它的最大獨立集包含的點數就是n-最大匹配數
************************************************
二分圖最大點權獨立集
定義
要求點權和最大的二分圖獨立集
解法:
超源向所有x部的點連邊,容量為該點點權
y部所有點向超匯連邊,容量為該點點權
對於二分圖原有的邊,容量為inf
然後跑最大流即可
洛谷 P2774 方格取數問題
傳送門
題目描述
在一個有 m*n 個方格的棋盤中,每個方格中有一個正整數。現要從方格中取數,使任意 2 個數所在方格沒有公共邊,且取出的數的總和最大。試設計一個滿足要求的取數算法。對於給定的方格棋盤,按照取數要求編程找出總和最大的數。
輸入格式:
第 1 行有 2 個正整數 m 和 n,分別表示棋盤的行數和列數。接下來的 m 行,每行有 n 個正整數,表示棋盤方格中的數。
輸出格式:
程序運行結束時,將取數的最大總和輸出
輸入樣例
3 3
1 2 3
3 2 3
2 3 1
輸出樣例
11
說明
m,n<=100
***********************************
分析
這題要求所取的方格不相鄰
那麽對每個格子黑白染色
我們由黑點向所有與他相鄰的白點連邊
就構成了一個二分圖
到這裏不難發現題目要求的就是二分圖的最大點權獨立集
點權!!!是點權!!!
所以這裏我們就用上述方法解決
所有黑點向超匯連邊,容量為該點點權
超源向所有白點連邊,容量為該點點權
對於上面所述用於構成二分圖的邊,容量為inf
然後跑最大流就好
#include<iostream> #include<cstdio> #include<vector> #include<queue> #include<algorithm> #include<cstring> using namespace std; int read() { int f=1,x=0; char ss=getchar(); while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();} while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();} return f*x; } const int inf=1e9; int n,m; int s=0,t; int col[110][110]; struct node{int v,f,nxt;}E[1000010]; int head[100010],tot=1;; int lev[100010]; int addx[]={0,0,1,-1},addy[]={1,-1,0,0}; int sum,ans; void add(int u,int v,int f) { E[++tot].nxt=head[u]; E[tot].f=f; E[tot].v=v; head[u]=tot; } bool bfs() { memset(lev,-1,sizeof(lev)); lev[s]=0; queue<int> q; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=E[i].nxt) { int v=E[i].v; if(lev[v]==-1&&E[i].f) { lev[v]=lev[u]+1; if(v==t) return true; q.push(v); } } } return false; } int dfs(int u,int cap) { if(u==t) return cap; int flow=cap; for(int i=head[u];i;i=E[i].nxt) { int v=E[i].v; if(lev[v]==lev[u]+1&&E[i].f>0&&flow) { int f=dfs(v,min(E[i].f,flow)); flow-=f; E[i].f-=f; E[i^1].f+=f; } } return cap-flow; } int main() { n=read();m=read(); t=n*m+1; for(int i=1;i<=n;i++)//黑白染色,1為黑,0為白 for(int j=1;j<=m;j++) { if( (i%2 && j%2) || (i%2==0 && j%2==0)) col[i][j]=1; else col[i][j]=0; } for(int i=1;i<=n;i++)//構圖 { for(int j=1;j<=m;j++) { int x=read(); sum+=x; int num=m*(i-1)+j;//計算該點的編號 if(col[i][j]) add( num,t,x ),add( t,num,0 );//黑點向超匯連邊 else { add(s,num,x);add(num,s,0);//超源向白點連邊 for(int k=0;k<4;k++)//原二分圖中的邊 { int nx=i+addx[k],ny=j+addy[k]; if(nx>0&&nx<=n &&ny>0&&ny<=m) add(num,m*(nx-1)+ny,inf),add(m*(nx-1)+ny,num,0); } } } } while(bfs())//最大流 ans+=dfs(s,inf); cout<<sum-ans; return 0; }
BZOJ 1143 [CTSC2008]祭祀river
傳送門
Description
在遙遠的東方,有一個神秘的民族,自稱Y族。他們世代居住在水面上,奉龍王為神。每逢重大慶典, Y族都
會在水面上舉辦盛大的祭祀活動。我們可以把Y族居住地水系看成一個由岔口和河道組成的網絡。每條河道連接著
兩個岔口,並且水在河道內按照一個固定的方向流動。顯然,水系中不會有環流
由於人數眾多的原因,Y族的祭祀活動會在多個岔口上同時舉行。出於對龍王的尊重,這些祭祀地點的選擇必
須非常慎重。準確地說,Y族人認為,如果水流可以從一個祭祀點流到另外一個祭祀點,那麽祭祀就會失去它神聖
的意義。族長希望在保持祭祀神聖性的基礎上,選擇盡可能多的祭祀的地點。
Input
第一行包含兩個用空格隔開的整數N、M,分別表示岔口和河道的數目,岔口從1到N編號。
接下來M行,每行包含兩個用空格隔開的整數u、v,
描述一條連接岔口u和岔口v的河道,水流方向為自u向v。
N≤100M≤1000
Output
第一行包含一個整數K,表示最多能選取的祭祀點的個數。
Sample Input
4 4
1 2
3 4
3 2
4 2
Sample Output
2
【樣例說明】
在樣例給出的水系中,不存在一種方法能夠選擇三個或者三個以上的祭祀點。包含兩個祭祀點的測試點的方案有兩種:
選擇岔口1與岔口3(如樣例輸出第二行),選擇岔口1與岔口4。
水流可以從任意岔口流至岔口2。如果在岔口2建立祭祀點,那麽任意其他岔口都不能建立祭祀點
但是在最優的一種祭祀點的選取方案中我們可以建立兩個祭祀點,所以岔口2不能建立祭祀點。對於其他岔口
至少存在一個最優方案選擇該岔口為祭祀點,所以輸出為1011。
****************************
分析:
簡化題目意思
就是選取一個點集,其中任意兩點不在一條鏈上
並使得點數最大
這不就是圖的最大獨立集嘛!!!
然而要怎麽處理兩點是否在一條鏈上呢
用傳遞閉包+floyd的思想
讀入時對於一條u到v的邊,使map[u][v]=1
再利用floyd將連通性傳遞
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map[i][j]|=(map[i][k]&map[k][j]);
傳遞完後直接在圖上跑二分圖最大匹配就好了
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
return f*x;
}
const int maxn=110;
int n,m;
int map[maxn][maxn];
bool vis[maxn];
int match[maxn];
int ans;
bool dfs(int u)
{
for(int v=1;v<=n;v++)
{
if(!map[u][v]||vis[v]) continue;
vis[v]=true;
if(!match[v]||dfs(match[v]))
{
match[v]=u;
return true;
}
}
return false;
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;++i)
{
int u=read(),v=read();
map[u][v]=1;
}
for(int k=1;k<=n;k++)//floyd實現傳遞閉包
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map[i][j]|=(map[i][k]&map[k][j]);
for(int i=1;i<=n;i++)
{
memset(vis,false,sizeof(vis));
if(dfs(i))ans++;
}
cout<<n-ans;
return 0;
}
?
洛谷P2774 方格取數問題 BZOJ 1143祭祀river【二分圖最大獨立集】