【优化】Problem 800: 可怜的火鸡

4 篇文章 0 订阅

Problem 800: 可怜的火鸡
Time Limit: 1000 ms Memory Limit: 524288 KB 捆绑测试

Problem Description
有N只火鸡,有M次操作。每次操作的对象是Ai和Bi:

若Ai和Bi都还没被吃掉,选一只吃掉
若Ai和Bi都被吃掉了,则忽略这一次操作
若Ai和Bi只有一只还没被吃掉,则吃掉这一只
小J想知道在操作后有多少对(x,y)火鸡有可能还存活
N≤400,M≤10^5

【数据范围及子任务】

本题采用捆绑测试,你只有通过一个子任务的所有数据点才能获得该子任务的全部分数,否则该子任务得0分。

2≤N≤400

1≤M≤10^5

1≤xi

题解

dfs暴力40分

答案与边的顺序有关
我们反过来推
若[x,y]存在
则和 x或y 有关系的边 在[x,y]这条边前 必须存在 且不重复 而且没有交集
O(n^2)枚举边
O(m)逆序遍历
总时间O(n^2*m)

必须优化
考虑 对于每个点 每次将边加进一个集合中(若重复则不存在这个点)
枚举两个点判断是否有交集
加边O(n*m)
枚举交集O(n^2*n)
总时间O(n^3+n*m)

参考文献

[1]AtCoder Grand Contest 016做题记录
[2]cs的比赛总结

ac代码(可以把集合记录下来空间换时间,这里只用bool)

#include<cstdio>
#include<iostream>
#define N 410
#define M 100010
using namespace std;
int n,m,u[M],v[M],ans;
bool b[N][N],bo[N],boo;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u[i],&v[i]);
    }
    for(int i=1;i<=n;i++){
        b[i][i]=1;
        for(int j=m;j>=1;j--){
            if(b[i][u[j]]&&b[i][v[j]]){
                bo[i]=1;
                break;
            }
            if(!b[i][u[j]]&&!b[i][v[j]])continue;
            if(b[i][u[j]])b[i][v[j]]=1;
            else b[i][u[j]]=1;
        }
    }
    for(int i=1;i<=n;i++){
        if(bo[i])continue;
        for(int j=i+1;j<=n;j++){
            if(bo[j]||b[i][j])continue;
            boo=0;
            for(int k=1;k<=n;k++)if(b[i][k]&&b[j][k]){
                boo=1;
                break;
            }
            if(!boo){
//              printf("%d %d\n",i,j);
                ans++;
            }
        }
    }
    printf("%d\n",ans);
}

dfs(40分)

#include<cstdio>
#include<iostream>
#define M 100010
#define N 410
using namespace std;
typedef long long LL;
int num,n,m,u[M],v[M],a[N],ans;
bool bo[N],b[N][N];
inline int rd(){
    char ch;
    int p=0,q=1;
    while((ch=getchar())<'0'||ch>'9')if(ch=='-')q=-1;
    while(ch>='0'&&ch<='9')p=p*10+ch-'0',ch=getchar();
    return p*q;
}
void dfs(int x){
//  printf("%d\n",x);
    if(x==m+1){
        int cnt=0;
        for(int i=1;i<=n;i++)if(!bo[i])a[++cnt]=i;
//      for(int i=1;i<=n;i++)printf("%d ",bo[i]);
//      printf("\n");
//      printf("%d\n",cnt);
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                if(i!=j&&!b[a[i]][a[j]]){
                    ans++;
                    b[a[i]][a[j]]=b[a[j]][a[i]]=1;
                }
        return ;
    }
    if(bo[u[x]]&&bo[v[x]])dfs(x+1);
    else if(bo[u[x]]){
        bo[v[x]]=1;
        dfs(x+1);
        bo[v[x]]=0;
    }
    else if(bo[v[x]]){
        bo[u[x]]=1;
        dfs(x+1);
        bo[u[x]]=0;
    }
    else {
        bo[u[x]]=1;
        dfs(x+1);
        bo[u[x]]=0;
        bo[v[x]]=1;
        dfs(x+1);
        bo[v[x]]=0;
    }
}
int main(){
    n=rd(); m=rd();
    for(int i=1;i<=m;i++){
        u[i]=rd();
        v[i]=rd();
    }
    dfs(1);
    printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值