码迷,mamicode.com
首页 > 其他好文 > 详细

codeforces 1242C/1243 E . Sum Balance

时间:2019-11-09 09:22:54      阅读:81      评论:0      收藏:0      [点我收藏+]

标签:nta   cli   onclick   thml   nbsp   efi   search   一个   space   

 

弄了一整晚 各种bug ...最后超时,差点放弃... 然后...没指望地 随意剪了个枝.,然后......居然做出来了...  

 

E. Sum Balance

题目大意:有k个箱子(1<=k<=15),在第i个箱子里有ni(1<=n<=5000)个整数 ( |aij|<=1e9)。所有整数都是不同的。对 每个箱子 都  进行这样的操作:从每个箱子中取出一个整数,并放入 另一个或原本的 箱子中。问能否进行这样子的操作使得 操作结束后,每个箱子的数字的和相等。

 

赛后补题,看了codeforces的标签:

 Problem tags:bitmasks,dfs and similar,dp,graphs,implementation。
 
dp实在太弱,不懂该怎么做,相信dp的做法会简单很多,然而我不会。
 
我是从graph的思想,用dfs的方法通过bitmasks记录状态来做的....
 
具体:
 
在最开始,计算sum和aver(平均值) sum模n有余则 直接输出no
 
注意题目的一句话:All of the integers are distinct.
所以就是 从每个box里取一个数出来,那么重新放回去的数值aij 的 i 和j 是唯一确定的。
所以对所有box里的所有数值 它 至多有 一个相对应的数值aij , 如果假想以有向边连接这样的对应关系,那么
正解必是   从1到n   覆盖了所有的数值的  1个或多个不相交环
 
环里边的顶点对应的是box的标号。
 
所以 做法是:
对于所有box的每个点,dfs去找它所在的环,并把 环 存进 环所覆盖的box标号的 容器里。
环用二进制状压去记录。
 
因为每个点题目保证了只有1条出边,所以,找到环的点在之后就不用再进行判断和查找。
 
之后就是搜索,判断有没有1个或多个环 或起来 的值 恰好是 n个1的二进制状态。有的话就是答案,找不到就是no。
然后是,适当剪枝。

 

技术图片
#include<bits/stdc++.h>
#define debug printf("!");
#define mp make_pair
using namespace std;
typedef long long ll;
const int maxn=5e3+50;
const int inf=0x3f3f3f3f;

int n,allstate;
ll a[16][maxn],sum,aver;
unordered_map<ll,pair<int,int>>mpp;

//tsum-tval+val=aver

struct P{
    int sta,id;
};
vector<P>vec[16];

bool vis[16][maxn];
bool dfs(int idi,int idj,int&sta,int sti,int stj)//找通过st的回路
{
    int num=a[idi][0];
    ll tsum=a[idi][num+1],val,tval=a[idi][idj];
    val=aver-tsum+tval;
    
    if(!mpp.count(val))return 0;

    pair<int,int>pa=mpp[val];
    if(vis[pa.first][pa.second])return 0;
    //所要访问的点有回路 这条回路不属于自己 并且 无法通过这个点找到新的回路 所以直接return false

    if(pa.first==idi&&pa.second!=idj)return 0;
    //在题意中 当前点要从box拿出去 然后将pa这个点放进来  可以是拿出去放进来同一个点 或者来自不同box的两个点 但不可能是来自同一box的不同点

    if(pa.first==sti&&pa.second==stj)
    {
        vis[idi][idj]=1;//找到回路 标记点 此后这个点不用再找回路了 因为1个点最多只有一条回路
        //因为 一个点所连的点是一个数值 题目原话:All of the integers are distinct.
        vec[idi].push_back(P{sta,idj});
        return 1;
    }
    if((1<<pa.first)&sta)return 0;
    //这个点所连的点在回路中存在  则这个sta是错的 不是单纯的回路

    sta|=(1<<pa.first);
    if(dfs(pa.first,pa.second,sta,sti,stj))
    {
        vis[idi][idj]=1;
        vec[idi].push_back(P{sta,idj});
        return 1;
    }
    return 0;
}

int ans[16];//ans[i]=j 把i集合中的j拿出去换
bool can[16][maxn];
bool cann[140000][15];
bool search(int sta)
{
    if(!sta)return 1;
    for(int i=1;i<=n;i++)
    {
        if((1<<i)&sta&&!cann[sta][i])
        {
            for(int j=0;j<vec[i].size();j++)
            {
                if(can[i][j])continue;
                if((sta&vec[i][j].sta)==vec[i][j].sta)
                {
                    if(search(sta-vec[i][j].sta))
                    {
                        ans[i]=vec[i][j].id;
                        return 1;
                    }
                }
                if(sta==allstate)can[i][j]=1;
            }
        }
        cann[sta][i]=1;
    }
    return 0;
}
int tans[16];
void last(int idi,int idj)//根据之前记录的状态记下答案
{
    int num=a[idi][0];
    ll tsum=a[idi][num+1],val,tval=a[idi][idj];
    val=aver-tsum+tval;
    pair<int,int> p=mpp[val];
    tans[p.first]=idi;
    if(ans[p.first]!=inf)return;
    ans[p.first]=p.second;
    last(p.first,p.second);
}

int main()
{
    int i,j,sta;
    ll tsum;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        allstate|=1<<i;
        scanf("%lld",&a[i][0]);
        for(j=1,tsum=0;j<=a[i][0];j++)
        {
            scanf("%lld",&a[i][j]);
            tsum+=a[i][j];
            mpp[a[i][j]]=mp(i,j);
        }
        sum+=tsum;
        a[i][j]=tsum;
    }
    if(sum%n)
    {
        puts("No");return 0;
    }
    aver=sum/n;
    for(i=1;i<=n;i++)
        for(j=1;j<=a[i][0];j++)
            if(!vis[i][j])
                dfs(i,j,sta=1<<i,i,j);
    for(i=1;i<=n;i++)
        if(!vec[i].size())
        {
            puts("No");return 0;
        }
    memset(ans,inf,sizeof(ans));
    if(search(allstate))
    {
        puts("Yes");
        for(i=1;i<=n;i++)
            if(ans[i]!=inf)last(i,ans[i]);
        for(i=1;i<=n;i++)
            printf("%lld %d\n",a[i][ans[i]],tans[i]);
    }
    else puts("No");
}
View Code

 

 
 

codeforces 1242C/1243 E . Sum Balance

标签:nta   cli   onclick   thml   nbsp   efi   search   一个   space   

原文地址:https://www.cnblogs.com/kkkek/p/11824202.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!