您的位置:首页 > 其它

coi 2013-2014 round 5 domine

2014-12-26 20:01 239 查看
对于这样一个问题我们可以这样来看,对于棋盘的一个点,假如在这个地方放一个骨牌,那么它能对那些造成影响呢?很明显它会对与这个点相邻的点造成影响。

这样我们把棋盘上的点黑白染色,黑点向白点连边,费用为负权值和,流量为1,这样我们就把这个问题转化成了最小费用最大流的问题,然后再设一个点向超级起点建一个流量为K的边。这样就能确保我们最多会放K个。

所以最小费用最大流可以解决这个问题。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 10000
#define ll long long
using namespace std;
int p=1;
ll ans;
int map
[4],n,dian
[4],dui
[4],bj
[4],zai
[10];
int head
,use
,dis
,que
,tot,s,t,s1,k;
struct data
{
int go,next,wgt,f;
}edge[200000];
struct hh
{
int f,da,num;
}jj
;
void build(int a,int b,int c,int d)
{
p++;
edge[p].go=b;
edge[p].next=head[a];
head[a]=p;
edge[p].wgt=c;
edge[p].f=d;
}
void addedge(int a,int b,int c,int d)
{
build(a,b,c,d);
build(b,a,-c,0);
}
void up()
{
int k=jj[t].da;
for (int i=t;i!=s1;i=jj[i].f)
{
int node=jj[i].num;
edge[node].f-=k;
edge[node^1].f+=k;
}
}
int spfa()
{
int tou=1,wei=1;
memset(use,0,sizeof(use));
for (int i=1;i<=t;i++) dis[i]=-1000000000;
que[1]=s1;
use[s1]=1;
dis[s1]=0;
jj[s1].da=12345678;
for (;tou<=wei;tou++)
{
int node=que[tou%N];
for (int i=head[node];i;i=edge[i].next)
{
if (dis[node]+edge[i].wgt>dis[edge[i].go]&&edge[i].f>0)
{
dis[edge[i].go]=dis[node]+edge[i].wgt;
jj[edge[i].go].f=node;
jj[edge[i].go].da=min(jj[node].da,edge[i].f);
jj[edge[i].go].num=i;
if (!use[edge[i].go])
{
use[edge[i].go]=1;
wei++;
que[wei%N]=edge[i].go;
}
}
}
use[node]=0;
}
return dis[t];
}
int main()
{
freopen("domine.in","r",stdin);
freopen("domine.out","w",stdout);
scanf("%d%d",&n,&k);
s1=9002;
s=9000;
t=s+1;
addedge(s1,s,0,k);
for (int i=1;i<=n;i++)
for (int j=1;j<=3;j++)
scanf("%d",&map[i][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=3;j++)
{
dui[i][j]=++tot;
dian[i][j]=++tot;
addedge(dui[i][j],dian[i][j],0,1);
}
for (int i=1;i<=n;i++)
{
if (i%2) bj[i][1]=1;
else bj[i][1]=0;
for (int j=2;j<=3;j++)
bj[i][j]=bj[i][j-1]^1;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=3;j++)
zai[i][j]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=3;j++)
{
if (bj[i][j]==1)
{
if (zai[i][j+1]) addedge(dian[i][j],dui[i][j+1],map[i][j]+map[i][j+1],1);
if (zai[i][j-1]) addedge(dian[i][j],dui[i][j-1],map[i][j]+map[i][j-1],1);
if (zai[i+1][j]) addedge(dian[i][j],dui[i+1][j],map[i][j]+map[i+1][j],1);
if (zai[i-1][j]) addedge(dian[i][j],dui[i-1][j],map[i-1][j]+map[i][j],1);<span style="font-family: Arial, Helvetica, sans-serif;">			addedge(s,dian[i][j],0,1);</span>
}
else
{
addedge(dian[i][j],t,0,1);
}
}
int time=0;
while (1)
{
int x=spfa();
time++;
if (time==k+1) break;
ans=ans+(ll)(x);
up();
}
printf("%I64d\n",ans);
return 0;
}
代码很丑。

接下来算下空间时间

一共有4N+3个点,大概8N的边,而由于是二分图,所以最小费用最大流速度会很快。

  还有另外一种算法。

  定义状态 F[I][J][K],表示前i行,放J个,当前行的状态为K对后面的影响,这样我们可以使用状态压缩DP来做。

 一位大牛的代码,我考试的时候只写了费用流。

<pre name="code" class="cpp">#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1000+10,lolo=0X7fffffff-1;

int n,kk,now,A,B,C;
int map[maxn][3];
int f[maxn][maxn][8];
void gengxin(int w,int zhi,int ge,int now){
if (ge+B>kk) return;
if (w>3){
//如果当前的位置大于3,则直接更新;
f[A+1][B+ge][now]=max(f[A+1][B+ge][now],f[A][B][C]+zhi);
return;
}
if ((C>>(w-1))&1) gengxin(w+1,zhi,ge,now);//如果当前是1则不更新下一位置因为不会对后面造成影响。否则就求出对下面的影响。
else{
if (w<=2&&(!((C>>w)&1))) gengxin(w+2,zhi+map[A][w]+map[A][w+1],ge+1,now);
gengxin(w+1,zhi+map[A][w]+map[A+1][w],ge+1,now|(1<<(w-1)));//放牌。
gengxin(w+1,zhi,ge,now);//不放。
}
}
void chuli(){
for(int i=0;i<=n;i++)
for (int j=0;j<=kk;j++)
for (int k=0;k<=7;k++)
if(f[i][j][k]!=-lolo)
{
A=i;B=j;C=k;
gengxin(1,0,0,0);
}
}
void pretreatment(){
for (int i=0;i<maxn;i++)
for (int j=0;j<maxn;j++)
for (int k=0;k<8;k++) f[i][j][k]=-lolo;
f[0][0][7]=0;
}
void reading(){
scanf("%d%d",&n,&kk);
for (int i=1;i<=n;i++)
for (int j=1;j<=3;j++)scanf("%d",&map[i][j]);
}
int main(){
freopen("domine.in","r",stdin);
freopen("domine.out","w",stdout);
reading();
pretreatment();
chuli();
printf("%d",f[n+1][kk][0]);
return 0;
}




   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  压缩 流量 dp