匈牙利算法是kuangbin的
poj 1274 poj 2239 //水題
poj 2584
這個還是有點想法的,把每種衣服拆成n件衣服(每件衣服都是一個單獨的節點),然後依次連接
下面是代碼,思路還是比較清晰的
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
/* **************************************************************************
//二分圖匹配(匈牙利算法的DFS實現)
//初始化:g[][]兩邊頂點的劃分情況
//建立g[i][j]表示i->j的有向邊就可以了,是左邊向右邊的匹配
//g沒有邊相連則初始化爲0
//uN是匹配左邊的頂點數,vN是匹配右邊的頂點數
//調用:res=hungary();輸出最大匹配數
//優點:適用於稠密圖,DFS找增廣路,實現簡潔易於理解
//時間複雜度:O(VE)
//***************************************************************************/
//頂點編號從0開始的
const int MAXN=400;
int uN,vN;//u,v數目
int g[MAXN][MAXN];
int linker[MAXN];
bool used[MAXN];
bool dfs(int u)//從左邊開始找增廣路徑
{
int v;
for(v=0;v<vN;v++)//這個頂點編號從0開始,若要從1開始需要修改
if(g[u][v]&&!used[v])
{
used[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{//找增廣路,反向
linker[v]=u;
return true;
}
}
return false;//這個不要忘了,經常忘記這句
}
int hungary()//xiongyali
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
for(u=0;u<uN;u++)
{
memset(used,0,sizeof(used));
if(dfs(u)) res++;
}
return res;
}
//******************************************************************************/
int F[100],A[100],tmp[50];
int judge(char t){
if(t=='S')return 1;
else if(t=='M')return 2;
else if(t=='L')return 3;
else if(t=='X')return 4;
else return 5;
}
int main()
{
int v;
int k,k1,k2;
char str[30];
while(~scanf("%s",&str)&&strcmp(str,"ENDOFINPUT")!=0)
{
scanf("%d",&uN);
memset(g,0,sizeof(g));
getchar();
for(int i=0;i<uN;i++)
{
char t1,t2;
scanf("%c%c",&t1,&t2);
getchar();
F[i]=judge(t1);
A[i]=judge(t2);
}
memset(tmp,0,sizeof(tmp));
for(int i=1;i<=5;i++){
scanf("%d",&tmp[i]);
tmp[i]+=tmp[i-1];
}
for(int i=0;i<uN;i++){
for(int j=F[i];j<=A[i];j++){
for(int k=tmp[j-1]+1;k<=tmp[j];k++)g[i][k]=1;
}
}
vN=tmp[5]+20;//剛開始這裏寫的是6*12+11,WA,後來又+5,莫名其妙AC了,我也不知道爲什麼......
int re=hungary();
getchar();
scanf("%s",&str);
getchar();
if(re==uN)printf("T-shirts rock!\n");
else printf("I'd rather not wear a shirt anyway...\n");
}
return 0;
}
poj 2536
這題是水題,可是老是A不了,我也不知道爲什麼,我的思路和代碼風格完全和題解並沒有太大區別
poj 2446
這題我是這樣寫的
然後老是WA怎麼也改不過來......
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
/* **************************************************************************
//二分圖匹配(匈牙利算法的DFS實現)
//初始化:g[][]兩邊頂點的劃分情況
//建立g[i][j]表示i->j的有向邊就可以了,是左邊向右邊的匹配
//g沒有邊相連則初始化爲0
//uN是匹配左邊的頂點數,vN是匹配右邊的頂點數
//調用:res=hungary();輸出最大匹配數
//優點:適用於稠密圖,DFS找增廣路,實現簡潔易於理解
//時間複雜度:O(VE)
//***************************************************************************/
//頂點編號從0開始的
const int MAXN=1500+50;
int uN,vN;//u,v數目
int g[MAXN][MAXN];
int linker[MAXN];
bool used[MAXN];
int maze[500][500];
bool dfs(int u)//從左邊開始找增廣路徑
{
int v;
for(v=0;v<vN;v++)//這個頂點編號從0開始,若要從1開始需要修改
if(g[u][v]&&!used[v])
{
used[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{//找增廣路,反向
linker[v]=u;
return true;
}
}
return false;//這個不要忘了,經常忘記這句
}
int hungary()//xiongyali
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
for(u=0;u<uN;u++)
{
memset(used,0,sizeof(used));
if(dfs(u))res++;
}
return res;
}
//******************************************************************************/
int n,m;
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
bool ok(int x,int y){
if(x<=0||x>n||y<=0||y>m||maze[x][y]==-1)return false;
return true;
}
void deal(){
uN=n*m+5;
vN=n*m+5;
memset(g,0,sizeof(g));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<4;k++){
int x=i+dir[k][0],y=j+dir[k][1];
if(ok(x,y))g[(i-1)*m+j-1][(x-1)*m+y-1]=1;
}
}
}
}
int main()
{
int k,x,y;
while(~scanf("%d%d%d",&n,&m,&k)){
memset(maze,0,sizeof(maze));
for(int i=1;i<=k;i++){
scanf("%d%d",&y,&x);
maze[x][y]=-1;
}
deal();
int re=hungary();
if((n*m-k)%2)printf("NO\n");
else if(re+k==n*m)printf("YES\n");
else printf("NO\n");
}
}
然後我就看了看kuangbin的思路,沒辦法,改的和他一樣過了......
其實我也不知道我之前的思路到低哪裏不對了,現在也不知道
以下是正確代碼
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=2500+50;
int uN,vN;//u,v數目
int g[MAXN][MAXN];
int linker[MAXN];
bool used[MAXN];
int maze[500][500],num[50][50];
bool dfs(int u)//從左邊開始找增廣路徑
{
int v;
for(v=0;v<vN;v++)//這個頂點編號從0開始,若要從1開始需要修改
if(g[u][v]&&!used[v])
{
used[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{//找增廣路,反向
linker[v]=u;
return true;
}
}
return false;//這個不要忘了,經常忘記這句
}
int hungary()//xiongyali
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
for(u=0;u<uN;u++)
{
memset(used,0,sizeof(used));
if(dfs(u))res++;
}
return res;
}
//******************************************************************************/
int n,m;
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
bool ok(int x,int y){
if(x<=0||x>n||y<=0||y>m||maze[x][y]==-1)return false;
return true;
}
int main()
{
int k,x,y;
while(~scanf("%d%d%d",&n,&m,&k)){
memset(maze,0,sizeof(maze));
memset(num,0,sizeof(num));
memset(g,0,sizeof(g));
for(int i=1;i<=k;i++){
scanf("%d%d",&y,&x);
maze[x][y]=-1;
}
int tol=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(maze[i][j]!=-1)num[i][j]=tol++;
}
}
uN=vN=tol;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(maze[i][j]!=-1)
{
int u=num[i][j];
if(i-1>0&&maze[i-1][j]!=-1)g[u][num[i-1][j]]=1;
if(j-1>0&&maze[i][j-1]!=-1)g[u][num[i][j-1]]=1;
if(i+1<=n&&maze[i+1][j]!=-1)g[u][num[i+1][j]]=1;
if(j+1<=m&&maze[i][j+1]!=-1)g[u][num[i][j+1]]=1;
}
int re=hungary();
if((n*m-k)%2)printf("NO\n");
else if(re==tol)printf("YES\n");
else printf("NO\n");
}
}
poj 3041
這個題我要好好寫寫題解(其實是看別人的)我自己想了想,感覺和二分圖實在是扯不上關係......
這是別人的說法
這個題目屬於標準的圖論題目,需要適當的轉換,建立適當的圖來進行結題。建立二分圖,設置x座標作爲A點集,y座標作爲B點集。那麼每個點轉化成連接A、B點集的邊。問題就轉化成最小點覆蓋問題,即若選定一個點,那麼於此點相連的所有邊都被選定,求滿足選定所有邊的最少的點集。最小點覆蓋問題可以用二分圖的最大匹配來解。最小點數=最大匹配數(此處不在證明,詳情請看上篇文章)。這樣通過構建二分圖求解最大匹配數,來求解該問題的最小點覆蓋。
然後這題就是大水題了......
這個題之前見過,總想着是不是個貪心,前人經驗證明貪心不可做
以下是代碼
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=2500+50;
int uN,vN;//u,v數目
int g[MAXN][MAXN];
int linker[MAXN];
bool used[MAXN];
int maze[500][500],num[50][50];
bool dfs(int u)//從左邊開始找增廣路徑
{
int v;
for(v=0;v<vN;v++)//這個頂點編號從0開始,若要從1開始需要修改
if(g[u][v]&&!used[v])
{
used[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{//找增廣路,反向
linker[v]=u;
return true;
}
}
return false;//這個不要忘了,經常忘記這句
}
int hungary()//xiongyali
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
for(u=0;u<uN;u++)
{
memset(used,0,sizeof(used));
if(dfs(u))res++;
}
return res;
}
//******************************************************************************/
int n,m;
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
bool ok(int x,int y){
if(x<=0||x>n||y<=0||y>m||maze[x][y]==-1)return false;
return true;
}
int main()
{
int n,k,x,y;
while(~scanf("%d%d",&n,&k)){
memset(g,0,sizeof(g));
uN=n+1,vN=n+1;
while(k--){
scanf("%d%d",&x,&y);
g[x--][y--]=1;
}
printf("%d\n",hungary());
}
}
poj 1325
這題不難,不過有個地方需要注意
就是初始狀態兩個機器都是狀態0,所以有個事要注意,就是所有和0相連的的邊都要刪掉......(就因爲這個WA了幾次)
poj 2226
這個題挺難想的
我是看別人的題解看出來的
以下是別人的分析
題意:R*C的矩陣表示一塊田地,'*'表示溼地,'.'表示草地。現在FJ要在田地上鋪木板蓋掉所有的溼地,露出所有的草地。每塊木板的寬度爲1,長度爲任意長。問FJ最少用幾塊木板就可以完成任務?
思路:看了半天,實在是沒思路,看了看別人的報告才明白。把它轉化爲一個二分圖,求最小點覆蓋。
但如何構成二分圖呢??
就是把每一行中不相連的一塊區域看成一個點,同理每一列也一樣。然後就有點像3041那題一樣。求它的最小點覆蓋
*.*. 按行1 0 2 0 按列 1 0 4 0
.*** 0 3 3 3 0 3 4 5
***. 4 4 4 0 2 3 4 0
..*. 0 0 5 0 0 0 4 0
下面是代碼#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,cnt1,cnt2,map[2520][2520],linker[2520],vis[2520];
int g1[60][60],g2[60][60],tg[60][60];
char str[60][60];
int DFS(int u){
int v;
for(v=1;v<=cnt2;v++)
if(map[u][v] && !vis[v]){
vis[v]=1;
if(linker[v]==-1 || DFS(linker[v])){
linker[v]=u;
return 1;
}
}
return 0;
}
int Hungary(){
int u,ans=0;
memset(linker,-1,sizeof(linker));
for(u=1;u<=cnt1;u++){
memset(vis,0,sizeof(vis));
if(DFS(u))
ans++;
}
return ans;
}
int main(){
//freopen("input.txt","r",stdin);
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<n;i++)
scanf("%s",str[i]);
memset(g1,0,sizeof(g1));
memset(g2,0,sizeof(g2));
cnt1=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
if(str[i][j]=='*'){
if(j==0)
g1[i][j]=++cnt1;
else if(str[i][j-1]=='*')
g1[i][j]=g1[i][j-1];
else
g1[i][j]=++cnt1;
}
}
cnt2=0;
for(int j=0;j<m;j++) //注意這裏
for(int i=0;i<n;i++){
if(str[i][j]=='*'){
if(i==0)
g2[i][j]=++cnt2;
else if(str[i-1][j]=='*')
g2[i][j]=g2[i-1][j];
else
g2[i][j]=++cnt2;
}
}
memset(map,0,sizeof(map));
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(str[i][j]=='*')
map[g1[i][j]][g2[i][j]]=1;
int ans=Hungary();
printf("%d\n",ans);
}
return 0;
}
但是其實精髓還是不由完全掌握,以後遇見還要注意......