• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
AC_Artist.zig_zag
然而我依然在补题、
博客园    首页    新随笔    联系   管理    订阅  订阅

Sset

神犇考题第三题:

设 S ={x|x∈Z,1≤x≤n}给定 N 个 S 的子集 Si,每个集合有一个权值 Wi。这 N 个集合满足其中任意 K 个集合的并集中至少有 K 个元素。请从中选取若干个集合(可以不选),使得其并集的元素个数恰等于选取的集合数,且这些集合的权值和最小。

考试的时候觉得这个题相比另外两个挺可写的,于是就使劲想,想出了最大权闭合图,想出了每个集合代表一个数字,可就是没想到把这两个联系起来,无奈,最后还是在包含关系的时候出搞错了,果然我还是太弱了,构造能力捉急。

首先有一个非常有用的条件,就是任意k个集合的并集中至少有k个元素,那么,如果有一个合法的方案,那么一定可以用一个单独的元素来代表这一个集合。

我们考虑拿到一个集合权值的代价,就是要将这个集合的所有元素都拿到(k个集合,k个元素),怎么办呢?我们只要找到所有元素表示的集合,把它们都拿到就行了。但与此同时我们拿到的集合可能会额外增加许多元素,那么把这些元素代表的集合也拿到,直到不会产生新的元素为止,这就是一个闭合图了。

于是把权值取反,跑一遍最大权闭合图就行了。

这个题精髓是条件的转化与模型的构造。

sset
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6 #define maxn 1000
  7 #define maxm 120000
  8 #define inf 2147483647
  9 using namespace std;
 10 struct et
 11 {
 12     int s,t,val,next;
 13 }e[maxm];
 14 int dis[maxn],gap[maxn],fir[maxn],last[maxn],g[maxn],s[maxm],t[maxm],next[maxm],mat[maxn],sign[maxn];
 15 int a[maxn][maxn];
 16 bool v[maxn];
 17 int n,m,tot,st,ed,num,top;
 18 
 19 bool match(int x)
 20 {
 21     for (int j=fir[x];j;j=next[j])
 22     {
 23         int k=t[j];
 24         if (!v[k])
 25         {
 26             v[k]=1;
 27             if (mat[k]==0||match(mat[k]))
 28             {
 29                 mat[k]=x;
 30                 return 1;
 31             }
 32         }
 33     }
 34     return 0;
 35 }
 36 
 37 int dfs(int now,int flow)
 38 {
 39     if (now==ed) return flow;
 40     int sap=0;
 41     for (int j=last[now];j;j=e[j].next)
 42     {
 43         int k=e[j].t;
 44         if (e[j].val&&dis[now]==dis[k]+1)
 45         {
 46             last[now]=j;
 47             int tmp=dfs(k,min(e[j].val,flow-sap));
 48             e[j].val-=tmp;
 49             e[j^1].val+=tmp;
 50             sap+=tmp;
 51             if (sap==flow) return sap;
 52         }
 53     }
 54     if (dis[st]>=num) return sap;
 55     if (!(--gap[dis[now]])) dis[st]=num;
 56     ++gap[++dis[now]];
 57     last[now]=fir[now];
 58     return sap;
 59 }
 60 
 61 void add(int x,int y,int z)
 62 {
 63     e[++tot].s=x; e[tot].t=y; e[tot].val=z; e[tot].next=fir[x]; fir[x]=tot;
 64     e[++tot].s=y; e[tot].t=x; e[tot].val=0; e[tot].next=fir[y]; fir[y]=tot;
 65 }
 66 
 67 int main()
 68 {
 69     //freopen("sset.in","r",stdin);
 70     //freopen("sset.out","w",stdout);
 71     scanf("%d",&n);
 72     //构造二分图
 73     tot=0;
 74     for (int i=1;i<=n;i++)
 75     {
 76         scanf("%d",&g[i]);
 77         for (int j=1;j<=g[i];j++)
 78         {
 79             scanf("%d",&a[i][j]);
 80             s[++tot]=i; t[tot]=a[i][j]; next[tot]=fir[i]; fir[i]=tot;
 81         }
 82     }
 83     //跑完美匹配
 84     for (int i=1;i<=n;i++)
 85     {
 86         memset(v,0,sizeof(v));
 87         match(i);
 88     }
 89     for (int i=1;i<=n;i++) sign[mat[i]]=i;
 90     memset(fir,0,sizeof(fir));
 91     //构造最大权闭合图
 92     st=0; ed=n+1; num=n+2; tot=1;
 93     for (int i=1;i<=n;i++)
 94         for (int j=1;j<=g[i];j++)
 95             if (a[i][j]!=sign[i]) add(i,mat[a[i][j]],inf);
 96     int z,sum=0;
 97     for (int i=1;i<=n;i++)
 98     {
 99         scanf("%d",&z);
100         z=-z;
101         if (z>0) add(st,i,z),sum+=z;else add(i,ed,-z);
102     }
103     //跑网络流
104     memset(dis,0,sizeof(dis));
105     memset(gap,0,sizeof(gap));
106     gap[0]=num;
107     for (int i=st;i<=ed;i++) last[i]=fir[i];
108     int ans=0;
109     while (dis[st]<num) ans+=dfs(st,inf);
110     if (sum-ans>0) printf("%d\n",-(sum-ans));
111     else printf("%d\n",0);
112     return 0;
113 }

 

AC without art, no better than WA !
posted @ 2013-05-04 16:21  Zig_zag  阅读(493)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3