[POJ 1149]PIGS
这个题是经典的网络流题目。在经过一次次的思想过滤后你会发现,这个问题的瓶颈(换猪)大可以理解为前面的顾客给后面的留一些不是?这样问题就简单多了。
按顺序整理出到访每个猪圈的顾客,对于到访每个猪圈的第一个顾客,从[tex]S[/tex]向其连一条容量为此猪圈中猪的数量的边。然后每个猪圈前面一个顾客要个给后面的留一些猪,这个可以直接连一条容量为无限大的边来表示。
最后每个顾客向[tex]T[/tex]连一条容量为其买猪数量上限的边,然后求一遍[tex]S[/tex]到[tex]T[/tex]的最大流,问题得解。
这个题还有一个优化就是,有一些边是可以合并的(比如说从[tex]S[/tex]流出的一些边是可能指向同一顾客的,同一对顾客之间的连边数可能不止1条),但是我没有做这个优化,照样A了……
代码:
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using std::vector;
using std::queue;
using std::min;
const int maxn=105,maxm=1005;
namespace Dinic{
struct Edge{
int u,v,cap,flow;
};
vector<Edge> edges;
vector<int> G[maxn];
int s,t;
int m;
inline void AddEdge(int u,int v,int cap){
edges.push_back((Edge){u,v,cap,0});
edges.push_back((Edge){v,u,0,0});
m=edges.size();
G[u].push_back(m-2);
G[v].push_back(m-1);
}
bool vis[maxn];
int d[maxn];
inline bool bfs(){
register int i,u,siz;
queue<int> Q;
memset(vis,0,sizeof(vis));
d[s]=0;
Q.push(s);
vis[s]=true;
while(!Q.empty()){
u=Q.front();Q.pop();
siz=G[u].size();
for(i=0;i<siz;i++){
Edge& e=edges[G[u][i]];
if(!vis[e.v] && e.cap>e.flow){
d[e.v]=d[u]+1;
Q.push(e.v);
vis[e.v]=true;
}
}
}
return vis[t];
}
int cur[maxn];
int dfs(int x,int a){
if(x==t || a==0){
return a;
}
int f,flow=0,siz=G[x].size();
for(int& i=cur[x];i<siz;i++){
Edge& e=edges[G[x][i]];
if(d[e.v]==d[x]+1){
f=dfs(e.v,min(a,e.cap-e.flow));
if(f>0){
flow+=f;
e.flow+=f;
edges[G[x][i]^1].flow-=f;
a-=f;
if(a==0){
break;
}
}
}
}
if(a>0){
d[x]=-1;
}
return flow;
}
inline int solve(int S,int T){
register int ans=0;
s=S;t=T;
while(bfs()){
memset(cur,0,sizeof(cur));
ans+=dfs(s,0x7fffffff);
}
return ans;
}
};
using Dinic::AddEdge;
using Dinic::solve;
int pigs[maxm];
vector<int> Use[maxm];
int main(){
register int i,j;
int m,n;
scanf("%d%d",&m,&n);
for(i=1;i<=m;i++){
scanf("%d",&pigs[i]);
}
for(i=1;i<=n;i++){
int a,b,temp;
scanf("%d",&a);
for(j=1;j<=a;j++){
scanf("%d",&temp);
Use[temp].push_back(i);
}
scanf("%d",&b);
AddEdge(i,n+1,b);
}
for(i=1;i<=m;i++){
AddEdge(0,Use[i][0],pigs[i]);
int siz=Use[i].size();
for(j=1;j<siz;j++){
AddEdge(Use[i][j-1],Use[i][j],0x7f7f7f7f);
}
}
printf("%d\n",solve(0,n+1));
return 0;
}
[POJ 3974]Palindrome
Manacher……妙,妙啊……
这个题就要是给一个字符串,输出最大回文字串长度。Manacher即可。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1000005;
char buf[maxn],s[maxn<<1];
int p[maxn<<1];
inline void FactStr(){
register int i,j=0,n=strlen(buf);
s[j++]='$';
s[j++]='#';
for(i=0;i<n;i++){
s[j++]=buf[i];
s[j++]='#';
}
s[j]='\0';
}
inline int Manachar(){
register int i,n=strlen(s);
register int mx=0,id;
register int ans=-1;
for(i=1;i<n;i++){
if(i<mx)
p[i]=min(p[2*id-i],mx-i);
else
p[i]=1;
while(s[i-p[i]]==s[i+p[i]])
p[i]++;
if((p[i]+i)>mx){
id=i;
mx=p[i]+i;
}
ans=max(ans,p[i]-1);
}
return ans;
}
int main(){
register int Case=0;
while(scanf("%s",buf)==1){
if(buf[0]=='E')
break;
Case++;
FactStr();
printf("Case %d: %d\n",Case,Manachar());
}
return 0;
}
[POJ 2446]Chessboard
很容易看出是二分图最大匹配,可是怎么建图呢……
我们可以考虑把一个点的横纵坐标相加,和为偶数者称之为偶数点,反之为奇数点。我们发现,每个骨牌都正好覆盖了一个奇数点和一个偶数点。于是乎就能把点分为两个集合了,建图就很方便了。
代码:
#include <cstdio>
#include <cstring>
const int maxn=35,maxm=520;
bool G[maxm][maxm];
// Hungray
int odd_cnt=0,even_cnt=0;
bool vis[maxm];
int GirlFriend[maxm];
bool DFS(int x){
int i;
for(i=1;i<=even_cnt;i++)
if(G[x][i] && !vis[i]){
vis[i]=true;
if(!GirlFriend[i] || DFS(GirlFriend[i])){
GirlFriend[i]=x;
return true;
}
}
return false;
}
inline int Hungray(){
register int i,ans=0;
for(i=1;i<=odd_cnt;i++){
memset(vis,0,sizeof(vis));
if(DFS(i))
ans++;
}
return ans;
}
int n,m;
bool isHole[maxn][maxn];
int ct[maxn][maxn];
inline int AddPoint(int a,int b,int x,int y){
if(ct[x][y])
if(!isHole[x][y])
G[ct[a][b]][ct[x][y]]=true;
}
int main(){
int k,x,y;
register int i,j,tot;
scanf("%d%d%d",&m,&n,&k);
tot=m*n-k;
if(1&tot){
puts("NO");
return 0;
}
for(i=1;i<=k;i++){
scanf("%d%d",&x,&y);
isHole[y][x]=true;
}
for(i=1;i<=m;i++)
for(j=1;j<=n;j++){
if(isHole[i][j])
continue;
if(1&(i+j)){
ct[i][j]=++odd_cnt;
}else{
ct[i][j]=++even_cnt;
}
}
for(i=1;i<=m;i++)
for(j=1;j<=n;j++){
if(!isHole[i][j] && 1&(i+j)){
AddPoint(i,j,i-1,j);
AddPoint(i,j,i+1,j);
AddPoint(i,j,i,j-1);
AddPoint(i,j,i,j+1);
}
}
if(Hungray()==(tot/2)){
puts("YES");
}else{
puts("NO");
}
return 0;
}
[POJ 2406]Power Strings
循环节又一题……用KMP性质搞搞即可。
注意特判(好像是句废话),再就没啥了……
代码:
#include <cstdio>
#include <cstring>
const int maxn=1e6+5;
char P[maxn];
int f[maxn];
int main(){
register int i,j,xhj,n;
while(scanf("%s",P)){
if(P[0]=='.')
break;
n=strlen(P);
f[0]=f[1]=0;
for(i=1;i<n;i++){
j=f[i];
while(j && P[j]!=P[i])
j=f[j];
f[i+1]=(P[j]==P[i]?j+1:0);
}
xhj=n-f[n];
if(!(n%xhj)){
printf("%d\n",n/xhj);
}else{
puts("1");
}
}
return 0;
}
[POJ 2784]Buy or Build
啊这题竟然在POJ上有……
枚举套餐子集是肯定的啦,但接下来呢?有的同学或许会想直接Kruskal求MST。但是估计会T。
有一个很有效的优化:先求一遍不加套餐的MST,然后接下来每次求MST的时候都只考虑这[tex]n-1[/tex]条边,这样就很快了。
需要注意的是,这[tex]n-1[/tex]以外的边就算加入了套餐也不会被考虑。因为无论加不加套餐,这些更大的边所能连接的连通分量总是可以被以前更小的边连接,所以这条边无论如何也不会被考虑啦……
代码写起来容易让人崩溃……所以说这个题很能锻炼代码能力和Debug能力。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=1005,maxq=8;
int n;
int p[maxn],rank[maxn];
int find_set(int x){
if(p[x]==x)
return x;
else
return p[x]=find_set(p[x]);
}
inline void link_set(int x,int y){
if(rank[x]>rank[y]){
p[y]=x;
}else{
p[x]=y;
if(rank[x]==rank[y])
rank[y]++;
}
}
inline void union_set(int x,int y){
link_set(find_set(x),find_set(y));
}
inline bool is_same(int x,int y){
return find_set(x)==find_set(y);
}
inline void init_set(){
register int i;
for(i=1;i<=n;i++)
p[i]=i;
memset(rank,0,sizeof(rank));
}
int Cost[maxq];
vector<int> V[maxq];
struct Edge{
int u,v,d;
bool operator <(const Edge& x) const{
return d<x.d;
}
};
vector<Edge> bj;
vector<Edge> E;
vector<Edge> garbage;
int MST(int cnt,vector<Edge>& E,vector<Edge>& to){
if(!cnt)
return 0;
register int i,ans=0;
to.clear();
for(i=0;i<E.size();i++){
if(!is_same(E[i].u,E[i].v)){
union_set(E[i].u,E[i].v);
ans+=E[i].d;
to.push_back(E[i]);
if(!(--cnt))
break;
}
}
return ans;
}
int zb[maxn][2];
inline int EucSqr(int x,int y){
int t1=zb[x][0]-zb[y][0],t2=zb[x][1]-zb[y][1];
t1*=t1;
t2*=t2;
return t1+t2;
}
int main(){
int T,q,num,u;
register int i,j,k,cnt,temp,ans;
scanf("%d%d",&n,&q);
for(i=0;i<q;i++){
scanf("%d%d",&num,&Cost[i]);
V[i].clear();
while(num--){
scanf("%d",&u);
V[i].push_back(u);
}
}
for(i=1;i<=n;i++){
scanf("%d%d",&zb[i][0],&zb[i][1]);
}
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++){
bj.push_back((Edge){i,j,EucSqr(i,j)});
}
init_set();
sort(bj.begin(),bj.end());
ans=MST(n-1,bj,E);
for(i=0;i<(1<<q);i++){
init_set();
temp=0;
cnt=n-1;
for(j=0;j<q;j++)
if(i&(1<<j)){
temp+=Cost[j];
for(k=1;k<V[j].size();k++){
if(!is_same(V[j][k],V[j][0])){
union_set(V[j][k],V[j][0]);
cnt--;
}
}
}
ans=min(ans,temp+MST(cnt,E,garbage));
}
printf("%d\n",ans);
return 0;
}
[POJ 2455]Secret Milking Machine
这个题要求最大边最小,很明显要二分答案。
考虑验证谓词[tex]C(x)[/tex]。我们可以将所有边权小于等于[tex]x[/tex]的边加入图,然后判断是否有[tex]T[/tex]条从1到N的不重复路径。
不过这个怎么做呢?我们可以把边加入(注意我们要加入的边都是无向边),并把容量设为1,从1到N跑一遍最大流,就是不重复路径条数。
为什么可以这样呢?每个边容量只有1,最多只能让一条路径使用并“推送”到终点,所以从1到N的最大流就是不重复路径条数辣。
代码:
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=205,maxp=40005;
#define REP(i,n) for(i=0;i<(n);i++)
#define REP_B(i,n) for(i=1;i<=(n);i++)
#define CL_ARR(x,v) memset(x,v,sizeof(x))
namespace Dinic{
struct Edge{
int u,v,cap,flow;
};
vector<Edge> edges;
vector<int> G[maxn];
int m;
inline void AddEdge(int u,int v,int cap){
edges.push_back((Edge){u,v,cap,0});
edges.push_back((Edge){v,u,0,0});
m=edges.size();
G[u].push_back(m-2);
G[v].push_back(m-1);
}
inline void ClearGraph(){
register int i;
edges.clear();
m=0;
REP(i,maxn){
G[i].clear();
}
}
bool vis[maxn];
int d[maxn],cur[maxn];
int s,t;
inline bool BFS(){
register int i,u;
queue<int> Q;
CL_ARR(vis,0);
Q.push(s);
d[s]=0;
vis[s]=true;
while(!Q.empty()){
u=Q.front();
Q.pop();
REP(i,G[u].size()){
Edge& e=edges[G[u][i]];
if(!vis[e.v] && e.cap>e.flow){
vis[e.v]=1;
d[e.v]=d[u]+1;
Q.push(e.v);
}
}
}
return vis[t];
}
int DFS(int x,int a){
if(x==t || a==0)
return a;
int flow=0,temp;
for(int& i=cur[x];i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(d[e.v]==d[x]+1){
temp=DFS(e.v,min(a,e.cap-e.flow));
if(temp>0){
e.flow+=temp;
edges[G[x][i]^1].flow-=temp;
flow+=temp;
a-=temp;
if(a==0)
break;
}
}
}
return flow;
}
inline int Maxflow(int S,int T){
s=S;
t=T;
register int ans=0;
while(BFS()){
CL_ARR(cur,0);
ans+=DFS(s,0x7f7f7f7f);
}
return ans;
}
};
struct Edge{
int u,v,d;
bool operator <(const Edge& x) const{
return d<x.d;
}
};
Edge EdgePool[maxp];
int n,p,t;
inline bool Check(int x){
register int i;
Dinic::ClearGraph();
REP_B(i,p){
if(EdgePool[i].d>x)
break;
Dinic::AddEdge(EdgePool[i].u,EdgePool[i].v,1);
Dinic::AddEdge(EdgePool[i].v,EdgePool[i].u,1);
}
if((Dinic::Maxflow(1,n))<t)
return false;
else
return true;
}
int main(){
register int i,L=0,M,R=0;
scanf("%d%d%d",&n,&p,&t);
REP_B(i,p){
scanf("%d%d%d",&EdgePool[i].u,&EdgePool[i].v,&EdgePool[i].d);
R=max(R,EdgePool[i].d+1);
}
sort(EdgePool+1,EdgePool+1+p);
while(L<R){
M=L+(R-L)/2;
if(Check(M))
R=M;
else
L=M+1;
}
printf("%d\n",L);
return 0;
}
[POJ 1679]The Unique MST
又是一个上百行代码的题……
这个题要求判断MST的唯一性。我们可以通过求非严格的次小生成树,来判断MST的唯一性。
非严格的次小生成树就是枚举考虑将MST外的哪条边加入替换MST中的一条边。替换的方法是,求出MST中待加入边两端路径上最大边,删掉之后再把待加入边加进去。假如有一个非严格次小生成树权值和和MST一样,就说明MST不唯一。
代码:
#include <cstdio>
#include <cstring>
#include <bitset>
#include <algorithm>
using namespace std;
const int maxn=105,maxm=10005;
#define REP(i,n) for(i=1;i<=(n);i++)
#define GRAPH_REP(i,u) for(i=first[(u)];i;i=next[i])
#define CL_ARR(x,v) memset(x,v,sizeof(x))
int first[maxn];
int next[205],to[205],dist[205];
int graph_cnt=0;
inline void AddEdge(int u,int v,int d){
graph_cnt++;
next[graph_cnt]=first[u];
first[u]=graph_cnt;
to[graph_cnt]=v;
dist[graph_cnt]=d;
}
inline void ClearGraph(){
CL_ARR(first,0);
CL_ARR(next,0);
CL_ARR(to,0);
CL_ARR(dist,0);
graph_cnt=0;
}
int anc[maxn][32],maxcost[maxn][32];
int dep[maxn];
void dfs(int x,int depth,int fa,int d){
int i;
dep[x]=depth;
anc[x][0]=fa;
maxcost[x][0]=d;
GRAPH_REP(i,x){
if(to[i]!=fa){
dfs(to[i],depth+1,x,dist[i]);
}
}
}
int n;
inline void process(){
register int i,j,a;
dfs(1,0,0,0);
for(j=1;(1<<j)<n;j++){
REP(i,n){
if(anc[i][j-1]!=-1){
a=anc[i][j-1];
anc[i][j]=anc[a][j-1];
maxcost[i][j]=max(maxcost[i][j-1],maxcost[a][j-1]);
}
}
}
}
int query(int x,int y){
register int tmp,log,i,ans=-0x7fffffff;
if(dep[x]<dep[y])
swap(x,y);
for(log=1;(1<<log)<=dep[x];log++);
log--;
for(i=log;i>=0;i--)
if(dep[x]-(1<<log)>=dep[y]){
ans=max(ans,maxcost[x][i]);
x=anc[x][i];
}
if(x==y)
return ans;
for(i=log;i>=0;i--)
if(anc[x][i]!=-1 && anc[x][i]!=anc[y][i]){
ans=max(ans,maxcost[x][i]);
x=anc[x][i];
ans=max(ans,maxcost[y][i]);
y=anc[y][i];
}
ans=max(ans,maxcost[x][0]);
ans=max(ans,maxcost[y][0]);
return ans;
}
int p[maxn],rank[maxn];
int find_set(int x){
if(p[x]==x)
return x;
else
return p[x]=find_set(p[x]);
}
void link_set(int x,int y){
if(rank[x]>rank[y]){
p[y]=x;
}else{
p[x]=y;
if(rank[x]==rank[y])
rank[y]++;
}
}
inline void union_set(int x,int y){
link_set(find_set(x),find_set(y));
}
inline bool is_same(int x,int y){
return find_set(x)==find_set(y);
}
inline void init_set(){
register int i;
REP(i,n)
p[i]=i;
CL_ARR(rank,0);
}
struct Edge{
int u,v,d;
bool operator <(const Edge& x) const{
return d<x.d;
}
};
#define ALL_FT(x) E[x].u,E[x].v
Edge E[maxm];
int m;
bitset<maxn> Choose;
int MST(){
register int i,ans=0,cnt=0;
ClearGraph();
init_set();
Choose.reset();
sort(E+1,E+1+m);
REP(i,m){
if(!is_same(ALL_FT(i))){
Choose[i]=true;
cnt++;
ans+=E[i].d;
union_set(ALL_FT(i));
AddEdge(ALL_FT(i),E[i].d);
AddEdge(E[i].v,E[i].u,E[i].d);
}
if(cnt==(n-1)){
break;
}
}
return ans;
}
int main(){
int T;
int u,v,d;
register int i,ans,temp;
bool OK;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
REP(i,m){
scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].d);
}
ans=MST();
CL_ARR(anc,-1);
process();
OK=true;
REP(i,m){
if(!Choose[i]){
temp=query(ALL_FT(i));
if(temp==E[i].d){
OK=false;
puts("Not Unique!");
break;
}
}
}
if(OK)
printf("%d\n",ans);
}
return 0;
}
[POJ 2388]Who's in the Middle
这题题面长得挺吓人的(英文……),不过就是让你求中位数……
我怀疑会有卡快排的数据,不过我用的是STL的sort(sort好像用的不是普通的快排)
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=10000;
int A[maxn];
int main(){
register int i;
int n;
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&A[i]);
sort(A,A+n);
printf("%d\n",A[n>>1]);
return 0;
}
[POJ 2104]K-th Number
我擦终于A了这题了!
主席树坑点多……
最好不要写指针主席树,不然容易TLE……(自带常数?)
并且注意,一定要把数组开大,开大,再开大,重要的事情说三遍
代码: