[GD_SGOI 摘取作物]费用流

【题目】

提交文件:pick.pas/.cpp

输入文件:pick.in

输出文件:pick.out

 

Feather的农场里有N*M块地,排列成N行,每行M块地。Feather在每块地里种植了不同的农作物。现在这些农作物都成熟了,可以摘取下来出售了。其中第i行第j列的地里的农作物的价值为W[i,j]

JackRabbit是Feather的好友,平时经常为Feather的农作物除草除虫。为了答谢JackRabbit,Feather决定把一部分农作物送给JackRabbit。JackRabbit很高兴,恨不得一下子把农场里的农作物摘空。

为了防止JackRabbit把农作物摘空,Feather提出了两个条件:

1.每行最多选取两块地;

2.每列最多选取两块地。

  这下子把JackRabbit难住了。如何在满足这两个条件的前提下,使得摘取的农作物的价值之和最大呢?

 

输入格式:

  第一行是两个整数(3≤N≤30,3≤M≤30)。

  以下N行每行M个整数,表示每块地的农作物的价值W[i,j](0≤W[i,j]≤10000)。

 

输出格式:

  输出一个数,表示在满足条件的前提下摘取的农作物的价值之和的最大值。

 

输入样例:

3 3

1 5 3

4 7 5

0 4 1

 

输出样例:

21

 

样例解释:

第一行选5和3,第二行选4和5,第三行选0和4,总和为21,是满足条件的最佳选取方案。

【算法分析】
本题是这套题里最脑残的题目。
就是二分图。左边的点是行,右边的点是列。
中间连容量为1,费用为a[i][j]的边。
左右两边与源汇连容量为2的边。
但是直接这样求最大费用最大流是会错误的。
为什么呢?
因为最大流不一定取得最大费用。
最大费用最大流是保证流量最大的情况下取得的费用最大。
于是我们需要将二分图左边的点都向汇连容量为2的边。这要可以允许“不选满”。
然后最后的流量必定是2*n (n是二分图左边的点数)。
又能保证费用最大。
【其它】
在交卷之前,我对我可爱的徒弟小肥闽曰:“你第三题不要错了啊~”
肥闽答曰:“第三题错了我自切JJ!”
然后,他果断错了1个点,就是因为没有处理上述那种情况,于是果断SB。要是数据再黑一点果断要挂。。。。
【CODE】
#include #include #include const int N=30005;
const int INF=1000000000;
struct edge{int x,y,c,w;edge *next,*op;}*ls[N],*fa[N];
int m,n,vs,vt,cost;
int d[N],Q[N],v[N];

void addedge(int x,int y,int c,int w){
edge *p=(edge*)malloc(sizeof(edge));
edge *q=(edge*)malloc(sizeof(edge));
p->x=x; p->y=y; p->c=c; p->w= w; p->next=ls[x]; ls[x]=p; p->op=q;
q->x=y; q->y=x; q->c=0; q->w=-w; q->next=ls[y]; ls[y]=q; q->op=p;
}

void init(){
int i,j,x;
scanf("%d%d",&m,&n);
for (i=1;i<=m;i++)
for (j=1;j<=n;j++){
scanf("%d",&x);
addedge(i,m+j,1,x);
}
vs=0; vt=m+n+1;
for (i=1;i<=m;i++){
addedge(vs,i,2,0);
addedge(i,vt,2,0);
}
for (i=1;i<=n;i++) addedge(m+i,vt,2,0);
}

bool spfa(){
int head=0,tail=1;
for (int i=vs;i<=vt;i++){
d[i]=-INF;
v[i]=0;
}
d[vs]=0; v[vs]=1; Q[1]=vs;
while (head!=tail){
head++; if (head>=N) head=0;
for (edge *t=ls[Q[head]];t;t=t->next)
if (t->c && d[t->x]+t->w>d[t->y]){
d[t->y]=d[t->x]+t->w;
fa[t->y]=t;
if (!v[t->y]){
v[t->y]=1;
tail++; if (tail>=N) tail=0;
Q[tail]=t->y;
}
}
v[Q[head]]=0;
}
if (d[vt]<=0) return false;
return true;
}

void change(){
int nf=INF;
for (int i=vt;i!=vs;i=fa[i]->x)
if (fa[i]->c cost+=nf*d[vt];
for (int i=vt;i!=vs;i=fa[i]->x){
fa[i]->c-=nf;
fa[i]->op->c+=nf;
}
}

void MCMF(){
cost=0;
while (spfa()) change();
printf("%dn",cost);
}

int main(){
freopen("pick.in","r",stdin);
freopen("pick.out","w",stdout);
memset(ls,0,sizeof(ls));
init();
MCMF();
}

[GD_SGOI 数字贴吧]AC自动机上的动态规划、高精度

【题目】

提交文件:num.pas/.cpp

输入文件:num.in

输出文件:num.out

 

在百度贴吧里输入关键词,就可以进入相应的贴吧参与该话题的讨论。然而,有一些关键词是不能作为贴吧名称的。我们把这些关键词称为敏感词。

如果一个字符串的任何子串(包括本身)都不是敏感词,那么这个字符串才可以作为贴吧名称。

有一些贴吧是直接用数字作为贴吧名称的。例如:“0”、“1”、“100”、“0123456789”。然而,数字里面也有敏感词。如果“00”是敏感词,则包含连续2个“0”的数字都不得作为贴吧名称。

你的任务是统计一下一共有多少个数字可以作为贴吧名称。

 

输入格式:

第一行是,表示贴吧名称的最大长度为N≤63),一共有M≤10)个数字是敏感词。

以下M行每行一个数字,长度都不超过5。

 

输出格式:

输出可以作为贴吧名称的数字个数。

 

输入样例:

3 10

2

3

4

5

6

7

8

9

10

11

 

输出样例:

6

 

样例解释:

只有“0”、“1”、“00”、“01”、“000”、“001”可以作为贴吧名称。

【算法分析】
很容易发现。就是AC自动机dp,貌似又叫DFA?
然后对给出的串建立AC自动机。
于是F[i][j]表示已建立的串长度为i,在自动机中的j状态,所能有的方案数。
然后转移的话,就是在自动机的每个状态那里,加一个0~9的字符,然后给“转移以后的状态”加上当前这个状态的方案数。(加法原理。。。)
但是“推出方案数”的状态不能是一个串的结尾,因为按题意不能匹配!

最后的答案就是所有F[i][j]之和。当然,j不能是一个串的结尾。
最后由于这题实在太不厚道,还要用高进度写F[i][j]。。。
太猥琐了。。。
【CODE】
#include #include #include const int N=55;
int n,m,tot,Q[N];
struct Trie_t{int son[10];int Final,next;}Tr[N];
int F[70][55][101];
char str[N];

void Insert(){
int L=strlen(str);
int p=1;
for (int i=0;i if (Tr[p].son[str[i]-‘0’]) p=Tr[p].son[str[i]-‘0’];
else{
tot++;
Tr[p].son[str[i]-‘0’]=tot;
p=tot;
}
Tr[p].Final++;
}

void init(){
tot=1;
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
scanf("%s",str);
Insert();
}
}

void makenext(){
int head=0,tail=1;
Q[1]=1;
while (head!=tail){
head++;
int &p=Q[head];
if (Tr[Tr[p].next].Final) Tr[p].Final=1;
for (int c=0;c<10;c++)
if (Tr[p].son){
int q=Tr[p].next;
while (q && !Tr[q].son) q=Tr[q].next;
if (q) Tr[Tr[p].son].next=Tr[q].son;
else Tr[Tr[p].son].next=1;
Q[++tail]=Tr[p].son;
}
}
}

void plus(int *A,int *B){
for (int i=1;i<=100;i++) A[i]+=B[i];
for (int i=100;i>1;i–){
A[i-1]+=A[i]/10;
A[i]%=10;
}
}

void output(int *A){
int i,j;
for (i=1;i<=100;i++)
if (A[i]){
for (j=i;j<=100;j++) printf("%d",A[j]);
printf("n");
return;
}
printf("0n");
}

void dp(){
memset(F,0,sizeof(F));
F[0][1][100]=1;
for (int L=0;L for (int i=1;i<=tot;i++) if (!Tr[i].Final)
for (int j=0;j<10;j++){
int p=i;
while (p && !Tr[p].son[j]) p=Tr[p].next;
if (!p) plus(F[L+1][1],F[L][i]);
else plus(F[L+1][Tr[p].son[j]],F[L][i]);
}
int ans[101];
memset(ans,0,sizeof(ans));
for (int i=1;i<=n;i++)
for (int j=1;j<=tot;j++) if (!Tr[j].Final)
plus(ans,F[i][j]);
output(ans);
}

int main(){
freopen("num.in","r",stdin);
freopen("num.out","w",stdout);
init();
makenext();
dp();
}

[GD_SGOI 三角阵]找规律、字符串变换

【题目】

提交文件:tri.pas/.cpp

输入文件:tri.in

输出文件:tri.out

 

  把3个相同的小三角形按如下方式连接起来就形成了一个一级三角阵。

  我们把位于顶端的小三角形标记为T,位于左端的小三角形标记为L,位于右端的小三角形标记为R。

  把3个一级三角阵按同样的方式连接起来就形成了一个二级三角阵。

  我们为顶端的三角阵的标记添加前缀T,为左端的三角阵的标记添加前缀L,为右端的三角阵的标记添加前缀R。

把3个二级三角阵按同样的方式连接起来就形成了一个三级三角阵。

同样地为顶端的三角阵的标记添加前缀T,为左端的三角阵的标记添加前缀L,为右端的三角阵的标记添加前缀R。

依次类推,可以构建一个N级三角阵。

如果两个小三角形有公共点,则认为这两个小三角形相邻,可以一步到达。

你的任务是求从一个小三角形走到另一个小三角形至少需要多少步。

 

输入格式:

第一行是三角阵的等级≤30)。

第二行和第三行都是一个长度为N的字符串,由大写字母“L”、“R”、“T”组成,表示两个小三角形的标号。

 

输出格式:

输出一个数,表示从一个小三角形走到另一个小三角形所需的最少步数。

 

输入样例:

3

TRL

RLR

 

输出样例:

5

 

样例解释:

从“TRL”出发,途经“TRR”、“RTT”、“RTL”、“RLT”,最后到达“RLR”,一共需要5步。

【算法分析】
首先我们围观它走一步的话,那个字符串会有什么变化。
第一种可能:"abc"->"abk" 然后这个这个c和k是可以任意的。
第二种可能:"xyzabbbbbbb…bbbbb"->"xyzbaaaaaaa…aaaaa",当然,这个a和b不能相同。
于是可以很直接地得到一个走到本三角顶点的算法。
那就是从高位到低位,一位一位地处理,具体自己YY吧,不难。
并且容易发现他是最优的。(怎么发现?看图。。。)
然后对于A串和B串,我们先把相同的前缀删掉。
对于剩下的东西而言。有两种可能。
剩下的设长度为n。
1、从我所属于的这个(n-1)级三角形直接走到对象的那个(n-1)级三角形。
2、从我所属于的这个(n-1)级三角形绕路另一个(n-1)级三角形,再跑到对象的那个(n-1)三角形。
于是分开两种情况,min一下即可。
【CODE】
#include #include #include typedef __int64 lld;
const int N=55;
int n;
char str1[N],str2[N],s1[N],s2[N];

lld solve(char *ss1,char *ss2){
memcpy(s1,ss1,sizeof(s1));
memcpy(s2,ss2,sizeof(s2));
int i,j,k;
lld ans=0;
for (i=1;i<=n;i++)
if (s1[i]!=s2[i]){
for (j=n;j>i;j–)
if (s2[i]!=s1[j]){
ans+=(lld)(1)<<(n-j);
s1[j]=s2[i];
}
ans++;
for (j=n;j>i;j–) s1[j]=s1[i];
s1[i]=s2[i];
}
return ans;
}

void work(){
if (strcmp(str1+1,str2+1)==0) puts("0");
else{
lld ans=solve(str1,str2);
char str[N];
memcpy(str,str2,sizeof(str));
int i=1,j;
while (str1[i]==str2[i]) i++;
if (str1[i]==’T’){
if (str2[i]==’L’) str[i]=’R’;
if (str2[i]==’R’) str[i]=’L’;
}
if (str1[i]==’L’){
if (str2[i]==’T’) str[i]=’R’;
if (str2[i]==’R’) str[i]=’T’;
}
if (str1[i]==’R’){
if (str2[i]==’L’) str[i]=’T’;
if (str2[i]==’T’) str[i]=’L’;
}
for (j=i+1;j<=n;j++) str[j]=str1[i];
lld now=solve(str1,str)+solve(str,str2);
if (now printf("%I64dn",ans);
}
}

int main(){
freopen("tri.in","r",stdin);
freopen("tri.out","w",stdout);
scanf("%d%s%s",&n,str1+1,str2+1);
work();
return 0;
}

[POI2005]Kos-Dicing【二分答案+最大流】

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1532
【算法分析】
二分答案然后,将比赛分配给选手,判定能不能分完。
这个用最大流实现。
【其它】sap被卡。HLPP被卡。。。于是dinic。。。
于是你可以见识到什么叫卡过。。。
34 30276 edward_mj 3164K 4772MS G++ 2.39K 2010-06-16 01:39:26 【CODE】
#include #include #include #define min(x,y) ((x)<(y)?(x):(y))
const int N=20005;
const int INF=0x7FFFFFFF;
struct edge{int x,y,c;edge *next,*op;}*ls[N],*cur[N],*fa[N];
int n,m,vs,vt,Flow;
int d[N],lx[N],ly[N],v[N],Q[N];

void init(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d",&lx[i],&ly[i]);
vs=0; vt=n+m+1;
}

inline void addedge(int x,int y,int c){
edge *p=(edge*)malloc(sizeof(edge));
edge *q=(edge*)malloc(sizeof(edge));
p->x=x; p->y=y; p->c=c; p->next=ls[x]; ls[x]=p; p->op=q;
q->x=y; q->y=x; q->c=0; q->next=ls[y]; ls[y]=q; q->op=p;
}

bool bfs(){
for (int i=vs;i<=vt;i++){
d[i]=INF;
v[i]=0;
cur[i]=ls[i];
}
d[vs]=1; Q[1]=vs;
for (int st=1,ed=1;st<=ed;st++)
for (edge *t=ls[Q[st]];t;t=t->next)
if (t->c && d[t->y]==INF){
d[t->y]=d[t->x]+1;
Q[++ed]=t->y;
if (t->y==vt) return true;
}
return false;
}

void change(){
int nf=INF;
for (int i=vt;i!=vs;i=fa[i]->x)
if (fa[i]->c for (int i=vt;i!=vs;i=fa[i]->x){
fa[i]->c-=nf;
fa[i]->op->c+=nf;
}
Flow+=nf;
}

void dinic(){
int i;
while (bfs()){
i=vs;
while (cur[vs]){
edge *&t=cur[i];
for (;t;t=t->next)
if (t->c && d[t->x]+1==d[t->y] && !v[t->y]) break;
if (t){
fa[t->y]=t;
i=t->y;
if (i==vt){
change();
i=vs;
}
}else{
v[i]=1;
if (i!=vs) i=fa[i]->x;
}
}
}
}

bool Can(int Limit){
for (int i=vs;i<=vt;i++)
while (ls[i]){
edge*t=ls[i];
ls[i]=t->next;
free(t);
}
for (int i=1;i<=m;i++){
addedge(vs,i,1);
addedge(i,m+lx[i],1);
addedge(i,m+ly[i],1);
}
for (int i=1;i<=n;i++) addedge(m+i,vt,Limit);
Flow=0;
dinic();
if (Flow==m) return true;
return false;
}

void solve(){
int l=m/n,r=m,mid;
while (l mid=(l+r)/2;
if (Can(mid)) r=mid;
else l=mid+1;
}
printf("%dn",l);
}

int main(){
init();
solve();
}

[ZOJ3348 Schedule]网络流

【题目大意】
知道之前的比赛结果,和将要打的对阵。问DD有没有可能得冠军?(不能是并列冠军)
【算法分析】
很直观的想法,剩余的对阵里,如果有DD,那么让DD赢。
否则设为未群定比赛。
然后最后构一个二分图,把比赛分给那些人。
那些人还能剩多少场允许胜,那么就连这么多容量的边。
大概这样。
【其它】。。。之前WA成SB,重写1Y。泪流满面。
居然跑了个第一,照个相,不然马上被刷下来了。

【CODE】
#include #include #include #define clr(a) memset(a,0,sizeof(a))
const int maxn=55;
const int N=20005;
struct edge{int x,y,c;edge *next,*op;};
struct Network_t{
edge *ls[N],*cur[N],*fa[N];
int d[N],gap[N],Flow,S,T;
void init(int n){
for (int i=0;i<=n;i++)
while (ls[i]){
edge *t=ls[i];
ls[i]=t->next;
free(t);
}
}

void addedge(int x,int y,int c){
edge *p=(edge*)malloc(sizeof(edge));
edge *q=(edge*)malloc(sizeof(edge));
p->x=x; p->y=y; p->c=c; p->next=ls[x]; ls[x]=p; p->op=q;
q->x=y; q->y=x; q->c=0; q->next=ls[y]; ls[y]=q; q->op=p;
}

void relabel(int p,int &n){
cur[p]=ls[p];
int mm=n;
for (edge *t=ls[p];t;t=t->next)
if (t->c && d[t->y] d[p]=mm+1;
}

void change(){
int NF=0x7FFFFFFF;
for (int i=T;i!=S;i=fa[i]->x)
if (fa[i]->c Flow+=NF;
for (int i=T;i!=S;i=fa[i]->x){
fa[i]->c-=NF;
fa[i]->op->c+=NF;
}
}

void sap(int n){
int i;
Flow=0;
for (i=0;i<=n;i++){
cur[i]=ls[i];
d[i]=0;
gap[i]=0;
}
gap[0]=n+1;
i=S;
while (d[S]<=n){
edge *&t=cur[i];
for (;t;t=t->next)
if (t->c && d[t->y]+1==d[t->x]) break;
if (t){
fa[t->y]=t;
i=t->y;
if (i==T){
change();
i=S;
}
}else{
if (!–gap[d[i]]) return;
relabel(i,n);
gap[d[i]]++;
if (i!=S) i=fa[i]->x;
}
}
}
}Network;

int n,m,cnt,NL;
int win[maxn];
char Name[maxn][22];

int Get(char *str){
int i,j;
bool flag;
for (i=1;i<=n;i++){
flag=true;
for (j=0;j<22;j++)
if (str[j]!=Name[i][j]){
flag=false;
break;
}
if (flag) return i;
}
n++;
for (j=0;j<22;j++) Name[n][j]=str[j];
return n;
}

void init(){
clr(Name);
clr(win);
Network.init(N-1);
char s1[22],s2[22],s3[22];
n=1; Name[1][0]=Name[1][1]=’D’;
for (int x,y,i=1;i<=m;i++){
clr(s1); clr(s2); clr(s3);
scanf("%s%s%s",s1,s2,s3);
x=Get(s1);
y=Get(s2);
if (s3[0]==’w’) win[x]++;
else win[y]++;
}

Network.S=0; Network.T=1; cnt=0;
scanf("%d",&m);
for (int x,y,i=1;i<=m;i++){
clr(s1); clr(s2);
scanf("%s%s",s1,s2);
x=Get(s1);
y=Get(s2);
if (x==1) win[1]++;
if (y==1) win[1]++;
if (x!=1 && y!=1){
cnt++;
Network.addedge(x,NL+cnt,1);
Network.addedge(y,NL+cnt,1);
Network.addedge(NL+cnt,Network.T,1);
}
}
}

bool solve(){
for (int i=2;i<=n;i++)
if (win[i]>=win[1]) return false;
for (int i=2;i<=n;i++)
Network.addedge(Network.S,i,win[1]-win[i]-1);
Network.sap(NL+cnt);
if (Network.Flow==cnt) return true;
return false;
}

int main(){
clr(Network.ls);
while (scanf("%d%d",&NL,&m)!=EOF){
init();
if (solve()) puts("Yes");
else puts("No");
}
}