【學術篇】SDOI2009 最優影象
阿新 • • 發佈:2019-01-10
又是一道辣雞卡常數題….
luogu上有些題的時限還是有毒的… 最後也只能靠O2過掉了…
不過給我原題當時的2s我隨便過給你看嘛, 哪怕評測姬慢50%都沒關係的.. 貼一下codevs的截圖…
你看最慢的點也就1.07s…… (畢竟程式自帶大常數←_←
好了不吐槽了, 我們來分析一下這道題吧…
其實我當時做的時候(好像還是做學校食堂的那次測試?)並不知道這是一道網路流…
然後就寫暴力滾粗…
但據說這是一種非常常見的建圖方式.. 我們還是分析題目條件.
我們在網路流裡面做到的題目都是求和的, 那麼我們就要想辦法把
轉換完之後就可以跑最大費用最大流了.那麼怎麼轉換呢?? 用對數!!!
我們高一的時候學過,
- 每一行, 每一列作為一個點, 分別放在兩邊.
- 行和列之間連一條流量為1, 費用為交點的概率的對數的邊, 表示這個點最多選一次, 選的話取這個概率.
- 源點向每個行對應的點連流量為這一行的黑畫素數, 費用為0的邊, 表示這一行要選這麼多個.
- 每個列對應的點向匯點連流量為這一列的黑畫素數, 費用為0的邊, 表示這一列要選這麼多個.
樣例建圖大約就是這個樣子(好像還並不是很清楚怎麼建嘛) (對數的底我就隨便取個
然後費用取反跑費用流就行了. 據說這個題只能用zkw過, 但我的zkw也沒過
程式碼:
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=205;
const int M=23333;
const int INF=0x7f7f7f7f;
inline int gn(int a=0,char c=0){
for(;c<'0'||c>'9';c=getchar());
for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
}
int v[N],nxt[M],to[M],fl[M],co[M],tot=1;
void buildedge(int x,int y,int flow,int cost){
to[++tot]=y; nxt[tot]=v[x]; v[x]=tot; fl[tot]=flow; co[tot]=cost;
to[++tot]=x; nxt[tot]=v[y]; v[y]=tot; fl[tot]=0; co[tot]=-cost;
}
int d[N],n,m,s,t,cost,p[N][N],a[N],b[N];
int q[M],H,T,i,j,k;
bool vis[N];
inline bool spfa(int s,int t){
memset(vis,0,sizeof(vis));
memset(d,0x7f,sizeof(d));
d[t]=0; H=0; T=1; vis[t]=1; q[T]=t;
while(H<T){
int x=q[++H]; vis[x]=0;
for(int i=v[x];i;i=nxt[i])
if(fl[i^1]&&d[to[i]]>d[x]-co[i]){
d[to[i]]=d[x]-co[i];
if(!vis[to[i]]) vis[to[i]]=1,q[++T]=to[i];
}
}
return d[s]<INF;
}
int dfs(int x,int mx,int s=0){ vis[x]=1;
if(x==t) return mx; int k;
for(int i=v[x];i;i=nxt[i])
if(!vis[to[i]]&&fl[i]&&d[to[i]]==d[x]-co[i]){
k=::dfs(to[i], mx-s>fl[i]?fl[i]:mx-s);
if(k) cost+=k*co[i],fl[i]-=k,fl[i^1]+=k,s+=k;
if(s==mx) break;
}
return s;
}
int mcmf(int flow=0){
while(::spfa(s, t)){ vis[t]=1;
while(vis[t]){
memset(vis,0,sizeof(vis));
flow+=::dfs(s, INF);
}
} return flow;
}
void findans(){
for(int i=1;i<=n;++i)
for(int j=v[i];j;j=nxt[j])
if(to[j]>n)
p[i][to[j]-n]=!fl[j];
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
putchar(48+p[i][j]);
putchar(10);
}
}
int main(){
n=gn(); m=gn(); s=0; t=n+m+1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
int k=gn();
if(k) ::buildedge(i, j+n, 1, (int)(-log2(k)*2333333));
}
for(int i=1;i<=n;++i)
::buildedge(s, i, gn(), 0);
for(int i=1;i<=m;++i)
::buildedge(i+n, t, gn(), 0);
mcmf(); findans();
}
最後, 卡常數是非常不對的一件事情…或者說我該去化驗血統了?