1、hdu4292
題解:最大流 因爲我們發現食物和飲料間沒有直接的聯繫,所以要用人來當中間點,但是如果直接連到人上,會讓食物和飲料流出奇怪的情況,所以把人拆點。
人和人之間連邊權爲1,食物和人 飲料和人都連邊爲1。
食物和S連食物個數,飲料和T連飲料個數。超級源點->食物->人->人'->飲料->超級匯點。
代碼:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define db double
#define EPS 1e-15
#define inf 0x3f3f3f3f
using namespace std;
const int N = 20005;
const int M = 200005;
int n, f, d, s, t;
int cnt, head[N], link[N], que[N], lv[N];
int nxt[M], to[M], v[M];
void add(int u,int vv,int w){
to[cnt]=vv,v[cnt]=w;
nxt[cnt]=head[u],head[u]=cnt++;
to[cnt]=u,v[cnt]=0;
nxt[cnt]=head[vv],head[vv]=cnt++;
}
int bfs(){
int kid,now,f=0,r=1,i;
memset(lv,0,sizeof(lv));
que[0]=s; lv[s]=1;
while (f<r){
now=que[f++];
for(int i=head[now];i!=-1;i=nxt[i]){
kid=to[i];
if (!lv[kid] && v[i]){
lv[kid]=lv[now]+1;
if (kid==t) return 1;
que[r++]=kid;
}
}
}
return 0;
}
int dfs(int now,int sum){
int kid,flow,rt=0;
if (now==t) return sum;
for (int i=link[now];i!=-1 && rt<sum;i=nxt[i]){
link[now]=i;
kid=to[i];
if (lv[kid]==lv[now]+1 && v[i]){
flow=dfs(kid,min(sum-rt,v[i]));
if (flow){
v[i]-=flow;
v[i^1]+=flow;
rt+=flow;
}
else lv[kid]=-1;
}
}
return rt;
}
char c[205];
void read(){
int a;
for (int i=1;i<=f;i++){
scanf("%d",&a);
add(s,i,a);
}
for (int i=1;i<=d;i++){
scanf("%d",&a);
add(i+900,t,a);
}
for (int i=1;i<=n;i++)
add(i+300,i+600,1);
getchar();
for (int i=1;i<=n;i++){
scanf("%s",c);
for (int j=0;j<f;j++)
if (c[j]=='Y')
add(j+1,i+300,1);
}
for (int i=1;i<=n;i++){
scanf("%s",c);
for (int j=0;j<d;j++)
if (c[j]=='Y')
add(i+600,j+901,1);
}
}
int dinic(){
int ans=0;
while (bfs()){
for (int i=0;i<=t;i++)
link[i]=head[i];
ans+=dfs(s,inf);
}
return ans;
}
int main(){
while (scanf("%d%d%d",&n,&f,&d)==3){
cnt=0;
memset(head,-1,sizeof(head));
s=0,t=1999;
read();
printf("%d\n",dinic());
}
return 0;
}
題目:有一張圖,其中n個點,m條雙向邊,邊有權值。所以從起點1走到終點n有一個最短時間。問:
(1)最少去掉多少條邊,使得從起點到終點的時間大於最短時間(走不到也算)
(2)最多去掉多少條邊,使得從起點到終點的時間仍然爲最短時間。
其中 n ≤ 2000; m ≤ 60000
題解:第二問明顯是m-(邊最少的最短路徑上的邊數),第一問我們可以聯想到是一個最小割,然後最小割最大流定理,構造邊流量爲1的新圖,跑最大流。
代碼:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define db double
#define EPS 1e-15
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 2010;
const int maxm = 60010;
const int N = 2010;
const int M = 60010;
struct node{
int v,c,flow,nxt;
}e[120020];
vector< pair<int, int> > G[N];
queue<int> Q;
int head[maxn],cnt,n,m;
int st,ed;
int dd[maxn],cc[maxn];
void spfa(int st,int ed){
dd[st]=cc[st]=0;
bool vis[maxn]={0};
while (!Q.empty()) Q.pop();
vis[st]=1;
Q.push(st);
while (!Q.empty()){
int u=Q.front(); Q.pop();
vis[u]=0;
for (int i=0;i<G[u].size();i++){
int v=G[u][i].first,c=G[u][i].second;
if (dd[u]+c==dd[v]) cc[v]=min(cc[v],cc[u]+1);
if (dd[u]+c<dd[v]){
cc[v]=cc[u]+1;
dd[v]=dd[u]+c;
if (!vis[v]) Q.push(v), vis[v]=1;
}
}
}
}
void add(int u,int v,int c){
e[++cnt].v=v; e[cnt].c=c;
e[cnt].flow=0; e[cnt].nxt=head[u];
head[u]=cnt;
e[++cnt].v=u; e[cnt].c=0;
e[cnt].flow=0; e[cnt].nxt=head[v];
head[v]=cnt;
}
void init(){
memset(head,-1,sizeof(head));
cnt=1;
st=1, ed=n;
for (int u=1;u<=n;u++){
for (int i=0;i<G[u].size();i++){
if (dd[u]+G[u][i].second==dd[G[u][i].first])
add(u,G[u][i].first,1);
}
}
}
int lv[maxn],num[maxn],q[maxn];
void bfs(){
int f=0,r=1;
for (int i=0;i<=n;i++){
lv[i]=0;
num[i]=0;
}
q[f]=st;
lv[st]=1;
num[st]=1;
while (f!=r){
int u=q[f++];
for (int i=head[u];i;i=e[i].nxt){
if (e[i^1].c==0 || lv[e[i].v]<maxn) continue;
lv[e[i].v]=lv[u]+1;
++num[lv[e[i].v]];
q[r++]=e[i].v;
}
}
}
int dinic(){
n+=3;
bfs();
int u,flow=0;
int cur[maxn],path[maxn];
for (int i=0;i<=n;i++)
cur[i]=head[i];
u=st;
while (lv[st]<n){
if (u==ed){
int augflow=inf;
for (int i=st;i!=ed;i=e[cur[i]].v)
augflow=min(augflow,e[cur[i]].c);
for (int i=st;i!=ed;i=e[cur[i]].v){
e[cur[i]].c-=augflow;
e[cur[i]^1].c+=augflow;
e[cur[i]].flow+=augflow;
e[cur[i]^1].flow-=augflow;
}
flow+=augflow;
u=st;
}
int i;
for (i=cur[u];i;i=e[i].nxt)
if(e[i].c>0 && lv[u]==lv[e[i].v]+1) break;
if (i){
cur[u]=i;
path[e[i].v]=i^1;
u=e[i].v;
}
else {
if (0==(--num[lv[u]])) break;
cur[u]=head[u];
int mindis=n;
for (int j=head[u];j;j=e[j].nxt)
if(e[j].c>0) mindis=min(mindis,lv[e[j].v]);
lv[u]=mindis+1;
++num[lv[u]];
if (u!=st)
u=e[path[u]].v;
}
}
n-=3;
return flow;
}
int main(){
while (~scanf("%d%d",&n,&m)){
for (int i=1;i<=n;i++){
G[i].clear();
dd[i]=cc[i]=inf;
}
int u,v,l;
for (int i=0;i<m;i++){
scanf("%d%d%d",&u,&v,&l);
G[u].push_back({v,l});
G[v].push_back({u,l});
}
spfa(1,n);
init();
printf("%d %d\n",dinic(),m-cc[n]);
}
return 0;
}
3、hdu4289
題目:有n個城市,有個小偷想從其中一個城市逃到另一個城市,警察想要堵截這個小偷,知道了在每個城市堵截的成本,問如何安排在哪些城市堵截可以使小偷一定會被抓住,而且成本最低。
題解:還是最小割模型,把城市拆點,然後有邊的兩個城市連inf,防止直接流過去。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define db double
#define EPS 1e-15
#define inf 1e10
using namespace std;
struct edge{
int to,w,nxt;
}e[20005*8];
int lv[2005],que[2005],val[2005];
int head[2005],cnt;
void add(int u,int v,int w){
e[cnt].to=v; e[cnt].w=w;
e[cnt].nxt=head[u]; head[u]=cnt++;
e[cnt].to=u; e[cnt].w=0;
e[cnt].nxt=head[v]; head[v]=cnt++;
}
int bfs(int s,int t){
memset(lv,0,sizeof(lv));
int f=0,tail=1;
lv[s]=1; que[f]=s;
while (f<tail){
int now=que[f++];
for (int i=head[now];i!=-1;i=e[i].nxt){
if (e[i].w && lv[e[i].to]==0){
lv[e[i].to]=lv[now]+1;
if (e[i].to==t) return 1;
que[tail++]=e[i].to;
}
}
}
return 0;
}
int dfs(int s,int t,int flow){
if (s==t) return flow;
int tmp,cost=0;
for (int i=head[s];i!=-1;i=e[i].nxt){
if (e[i].w && lv[e[i].to]==lv[s]+1){
tmp=dfs(e[i].to,t,min(flow-cost,e[i].w));
if (tmp>0){
e[i].w-=tmp;
e[i^1].w+=tmp;
cost+=tmp;
if (flow==cost) break;
}
else lv[e[i].to]=-1;
}
}
return cost;
}
int dinic(int s,int t){
int ans=0;
while (bfs(s,t))
ans+=dfs(s,t,inf);
return ans;
}
int main(){
int n,m,s,t;
while (~scanf("%d%d",&n,&m)){
cnt=0;
memset(head,-1,sizeof(head));
scanf("%d%d",&s,&t);
for (int i=1;i<=n;i++){
scanf("%d",&val[i]);
add(i,i+1000,val[i]);
}
int u,v;
while (m--){
scanf("%d%d",&u,&v);
add(u+1000,v,inf);
add(v+1000,u,inf);
}
int ss=2003,tt=2004;
add(ss,s,inf);
add(t+1000,tt,inf);
s=ss,t=tt;
printf("%d\n",dinic(s,t));
}
return 0;
}
4、hdu4888 & hdu4975
題目:給一個N行M列的數字矩陣的行和以及列和,每個元素的大小不超過9,問這樣的矩陣是否存在,是否唯一。
題解:這個題問是否存在很好想,S到行和連行和,列和到T連列和,行和到列和連9,但是問是否唯一這裏我真的想了好久……之前一直覺得複雜度不對……後來發現要對殘量網絡刪邊建新圖然後找環就是O(M)的了,如果不建新圖,每條邊要被判(N-1)*(M-1)+1次……爲什麼找環就能確定唯一呢……因爲如果你圖上有一個流量爲正點數爲偶數的環,說明數值可以在其中兩個點上流動,所以就不唯一。
代碼:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define db double
#define EPS 1e-15
#define inf 1e10
using namespace std;
const int MAXN=500*2+10;
const int MAXM=500*500+2*MAXN;
int n,m,sum;
int S,T;
int head[MAXN],cnt;
int cur[MAXN],lv[MAXN];
bool flag,only;
struct edge{
int from,to,w,flow,nxt;
}e[MAXM<<1];
void add(int u,int v,int w){
e[cnt].from=u;
e[cnt].to=v; e[cnt].w=w; e[cnt].flow=0;
e[cnt].nxt=head[u]; head[u]=cnt++;
e[cnt].from=v;
e[cnt].to=u; e[cnt].w=0; e[cnt].flow=0;
e[cnt].nxt=head[v]; head[v]=cnt++;
}
bool bfs(){
puts("yes");
memset(lv,-1,sizeof(lv));
queue<int>q;
q.push(S);
lv[S]=0;
while (!q.empty()){
int now=q.front();
q.pop();
for (int i=head[now];i!=-1;i=e[i].nxt){
int v=e[i].to;
if (lv[v]==-1 && e[i].w>e[i].flow){
lv[v]=lv[now]+1;
q.push(v);
}
}
}
return lv[T]!=-1;
}
int dfs(int u,int cap){
if (u==T || cap==0) return cap;
int flow=0,subflow;
for (int i=cur[u];i!=-1;i=e[i].nxt){
cur[u]=i;
int v=e[i].to;
if ((lv[v]==lv[u]+1) && (subflow=dfs(v,min(cap,e[i].w-e[i].flow)))>0){
flow+=subflow;
e[i].flow+=subflow;
e[i^1].flow-=subflow;
cap-=subflow;
if(cap==0) break;
}
}
return flow;
}
int dinic(){
int maxflow=0;
while(bfs()){
memcpy(cur,head,sizeof(head));
maxflow+=dfs(S,inf);
}
return maxflow;
}
void check1(){
if (flag) return ;
if (dinic()!=sum){
flag=1;
}
}
bool vis[MAXN];
bool checkO(int x,int f){
vis[x]=1;
for (int i=head[x];i;i=e[i].nxt){
int v=e[i].to;
if (v!=f){
if (vis[v]) return 1;
if (checkO(v,x)) return 1;
}
}
vis[x]=0;
return 0;
}
void checkonly(){
if (flag) return ;
memset(vis,0,sizeof(vis));
for (int i=1;i<=n;i++){
if (checkO(i,-1)){
only=0;
return ;
}
}
}
void add2(int u,int v){
e[cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt++;
}
void rebuild(){
memset(head,-1,sizeof(head));
int tot=cnt;
cnt=0;
for (int i=0;i<tot;i++){
if (e[i].w>e[i].flow){
add2(e[i].from,e[i].to);
}
}
}
int main(){
int T,cas=1;
scanf("%d",&T);
while (T--){
scanf("%d %d",&n,&m);
flag=0,only=1;
cnt=0;
memset(head,-1,sizeof(head));
int r,c,rsum=0,csum=0;
S=0; T=n+m+1;
for (int i=1;i<=n;++i){
scanf("%d",&r);
rsum+=r;
add(S,i,r);
}
for (int i=1;i<=m;++i){
scanf("%d",&c);
csum+=c;
add(i+n,T,c);
}
if (rsum!=csum){
flag=1;
break;
}
sum=rsum;
for (int i=1;i<=n;i++){
for (int j=m;j>=1;j--){
add(i,j+n,9);
}
}
check1();
rebuild();
checkonly();
printf("Case #%d: ",cas++);
if(flag) puts("So naive!");
else if (only) puts("So simple!");
else puts("So young!");
}
return 0;
}