今天这场要总结一下。
A.[NOI2009]变换序列
题目描述:
输入:
输出:
题解:
题意既然已知了所有的D(i,Ti),设其为Di,那么对于每个i,Ti的情况有两种:
①Ti=(i+Di)%n
②Ti=(i-Di+n)%n
那么从i向这两种情况连边,做一个二分图匹配就好了,如果匹配数小于n的话则不成立,输出No Answer,否则一定有解。为了使得解的字典序最小,我们建边的时候先建指向大的,再建指向小的(对于链表建边来说),然后在匈牙利算法找增广路的时候要从大的点开始找增广路。(打时间标记可以大量节约时间复杂度)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdlib>
#define LiangJiaJun main
#define INF 199912270
#define S 1001
#define T 1002
using namespace std;
int D[10004],n;
int tm;
int vis[10004],res[10004];
int lk[10004],h[10004],ne;
struct edge{
int to,nt;
}e[200004];
void add(int u,int v){
e[++ne].to=v;e[ne].nt=h[u];h[u]=ne;
}
bool dfs(int x){
for(int i=h[x];i;i=e[i].nt){
if(vis[e[i].to]!=tm){
vis[e[i].to]=tm;
if(lk[e[i].to]==-1||dfs(lk[e[i].to])){
lk[e[i].to]=x;
return 1;
}
}
}
return 0;
}
int w33ha(){
ne=0;
memset(vis,0,sizeof(vis));
memset(h,0,sizeof(h));
for(int i=0;i<n;i++)lk[i]=-1;
for(int i=0;i<n;i++)scanf("%d",&D[i]);
for(int i=0;i<n;i++){
int a,b;
a=(i+D[i])%n;
b=(i-D[i]+n)%n;
if(a<b)swap(a,b);
add(i,a);
add(i,b);
}
int ans=0;
tm=0;
for(int i=n-1;i>=0;i--){
++tm;
if(dfs(i))++ans;
}
if(ans!=n)return puts("No Answer"),0;
for(int i=0;i<n;i++)res[lk[i]]=i;
for(int i=0;i<n-1;i++)printf("%d ",res[i]);
printf("%d\n",res[n-1]);
return 0;
}
int LiangJiaJun(){
while(scanf("%d",&n)!=EOF)w33ha();
return 0;
}
B.[POJ3020]Antenna Placement
给一个H*W(H<=40,W<=10)的矩阵,矩阵上每个点不是
′∗′
′
∗
′
就是
′o′
′
o
′
,求问至少用多少个2*1的矩形可以覆盖所有
′∗′
′
∗
′
,矩形可以横着放也可以竖着放。
题解:
对于每一个
′∗′
′
∗
′
,我们可以将其与其周围所有
′∗′
′
∗
′
连边,然后答案就是最小边覆盖。即(点数-最大匹配数),由于这里每个点被算了两次,那么答案其实是(
′∗′
′
∗
′
个数*2 - 最大匹配数)/2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdlib>
#define LiangJiaJun main
#define INF 199912270
using namespace std;
int H,W,cnt,tm;
int vis[10004];
int lk[10004],h[10004],ne;
char mp[44][14];
int dx[4]={0,1,0,-1},
dy[4]={1,0,-1,0};
struct edge{
int to,nt;
}e[200004];
void add(int u,int v){
e[++ne].to=v;e[ne].nt=h[u];h[u]=ne;
}
int POI(int x,int y){
return (x-1)*W+y;
}
bool dfs(int x){
for(int i=h[x];i;i=e[i].nt){
if(vis[e[i].to]!=tm){
vis[e[i].to]=tm;
if((!lk[e[i].to])||dfs(lk[e[i].to])){
lk[e[i].to]=x;
return 1;
}
}
}
return 0;
}
int w33ha(){
ne=0;cnt=0;
memset(vis,0,sizeof(vis));
memset(h,0,sizeof(h));
memset(lk,0,sizeof(lk));
scanf("%d%d",&H,&W);
for(int i=1;i<=H;i++){
scanf("%s",mp[i]+1);
for(int j=1;j<=W;j++)
if(mp[i][j]=='*')++cnt;
}
for(int i=1;i<=H;i++){
for(int j=1;j<=W;j++){
if(mp[i][j]=='*'){
for(int k=0;k<4;k++){
int nowx=i+dx[k],nowy=j+dy[k];
if(nowx>H||nowx<=0||nowy>W||nowy<=0)continue;
if(mp[nowx][nowy]=='*')add(POI(i,j),POI(nowx,nowy));
}
}
}
}
int ans=0;
for(int i=1;i<=H;i++){
for(int j=1;j<=W;j++){
if(mp[i][j]=='o')continue;
tm++;
if(dfs(POI(i,j)))++ans;
}
}
printf("%d\n",((cnt<<1)-ans)>>1);
return 0;
}
int LiangJiaJun(){
int T;scanf("%d",&T);
while(T--)w33ha();
return 0;
}
C.[HDU1528]
二分图最大匹配
D.[POJ1274]
二分图最大匹配
E.[HDU1068]
最大点独立集,想法跟B一样
F.[HDU3729]
二分图最大匹配,注意字典序
I.[HDU2853]
KM算法,带权二分图最大匹配。为了使得尽量选择原配的边,如果(i,j)在原配中选过,则(i,j)边的权值设为w(i,j)*100+1,否则设为w(i,j)
然后求出的最大匹配x,x/100就是答案,x%100就是改了多少次。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdlib>
#define LiangJiaJun main
#define INF 1999122700
#define S 1001
#define T 1002
using namespace std;
int n,m,mp[54][54];
int slack[54],lk[54],lx[54],ly[54],a[54];
bool vx[54],vy[54];
bool dfs(int x){
vx[x]=1;
for(int i=1;i<=m;i++){
if(vy[i])continue;
if(mp[x][i]==lx[x]+ly[i]){
vy[i]=1;
if((!lk[i])||dfs(lk[i])){
lk[i]=x;
return 1;
}
}
else{
slack[i]=min(slack[i],lx[x]+ly[i]-mp[x][i]);
}
}
return 0;
}
int cnt=0,ans=0;
int KM(){
int temp;
memset(lx,0,sizeof(lx));
memset(ly,0,sizeof(ly));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
lx[i]=max(lx[i],mp[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)slack[j]=INF;
while(1){
memset(vx,0,sizeof(vx));
memset(vy,0,sizeof(vy));
if(dfs(i))break;
else{
temp=INF;
for(int j=1;j<=m;j++)
if(!vy[j])temp=min(temp,slack[j]);
for(int j=1;j<=n;j++){
if(vx[j])lx[j]-=temp;
}
for(int j=1;j<=m;j++){
if(vy[j])ly[j]+=temp;
else slack[j]-=temp;
}
}
}
}
return 0;
}
int w33ha(){
memset(mp,0,sizeof(mp));
memset(lk,0,sizeof(lk));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&mp[i][j]);
}
}
int rek=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
rek+=mp[i][a[i]];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)mp[i][j]*=100;
mp[i][a[i]]++;
}
ans=0;
KM();
for(int i=1;i<=m;i++){
ans+=mp[lk[i]][i];
}
printf("%d %d\n",(n-(ans%100)),(ans/100)-rek);
return 0;
}
int LiangJiaJun(){
while(scanf("%d%d",&n,&m)!=EOF)w33ha();
return 0;
}