BZOJ 3661: Hungry Rabbit

这道题挺不错的。。反正愚蠢的我没想到这么妙的东西

有种网络流做法 拆拆点搞一搞看看是不是网络流就好 因为bzoj把时限放到了100s 所以就让这种做法过了。。昨天有人还T了 卡了100s 网上面有这里就不写了

在staus有明显的用时分界线。。
我们来说说贪心 为什么我想不到贪心很多年了。。。

不难发现可以用 O(nm) 知道每只兔子在第几天开始能用多少天
这样 在每一天 显然我们尽量用 能用久一点的兔子是更优的

那么在每一天 我们就分别对上一天在用的 以及没在用的分别按能用多少天排个序 在用的从小到大排 没在用的从大到小排
交换前面L个就好了 记得判断一下大小
如果交换之后在用的里面有能用天数=0的 那就无解了
复杂度 O(nlognm)

#include<bits/stdc++.h>
#define me(a,x) memset(a,x,sizeof a)
using namespace std;
const int N=805;
inline int read(){
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x*f;
}
char a[N][N]; int n,m,p,k,c[N][N],r[N];
bool Cmp1(int A,int B){return r[A]>r[B];}
bool Cmp2(int A,int B){return r[A]<r[B];}
int a1[N],a2[N],ans[N][N]; bool in[N];
int main(){
    int i,j; n=read(),m=read(),k=read(),p=read();
    for(i=1;i<=n;++i)scanf("%s",a[i]+1);
    for(i=1;i<=n;++i)
        for(j=m;j;--j)
          if(a[i][j]=='1')c[i][j]=c[i][j+1]+1;
          else c[i][j]=0;
    int l1=0,l2;
    for(i=1;i<=n;++i){
        r[i]=c[i][1];
        if(c[i][1])a1[++l1]=i;
    }
    sort(a1+1,a1+1+l1,Cmp1);
    l1=k; for(i=1;i<=l1;++i)in[a1[i]]=1,ans[1][i]=a1[i];
    bool no=0;
    for(j=2;j<=m;j++){
        l2=0;
        for(i=1;i<=n;i++){
            r[i]=c[i][j];
            if(c[i][j] && !in[i])a2[++l2]=i;
        }
        sort(a1+1,a1+1+l1,Cmp2);
        sort(a2+1,a2+1+l2,Cmp1);
        for(i=1;i<=l1;i++){
            if(i>l2 || i>p){
                if(r[a1[i]]==0)no=1;
                break;
            }
            if(r[a1[i]]>=r[a2[i]])break;
            in[a1[i]]=0; in[a2[i]]=1;
            a1[i]=a2[i];
        }
        for(i=1;i<=k;i++)ans[j][i]=a1[i];
        if(no)break;
    }
    if(no)printf("1\n");
    else{
        for(i=1;i<=m;++i){
            for(j=1;j<=k;++j)printf("%d ",ans[i][j]);
            printf("\n");
        }
    }
    return 0;
}

你可能感兴趣的:(BZOJ 3661: Hungry Rabbit)