[Wannafly26B] 冥土追魂 [贪心]

赛时代码(WA)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
using namespace std;
#define ll long long
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
char frBB[1<<12]={},*frS=frBB,*frT=frBB;
inline ll read()
{
    ll x=0;char ch=0;bool w=0;
    while(!isdigit(ch))w|=(ch=='-'),ch=getchar();
    while(isdigit(ch))x=x*10+(ch-'0'),ch=getchar();
    return w?-x:x;
}
int N,M,K;
ll Ans=0;
ll vis[1005]={};
pair<pair<ll,ll>,int> AAn[1005]={};
pair<int,int> id[1005]={};
ll Aij[1005][1005]={};
bool cmp(ll a,ll b)
{
    return a>b;
}
pair<ll,ll> Lst[1005][1005]={};
int main()
{
    N=(int)read(),M=(int)read(),K=(int)read();
    for(int i=1;i<=N;++i)
    {
        for(int j=1;j<=M;++j)
        {
            Aij[i][j]=read();
        }
        sort(Aij[i]+1,Aij[i]+1+M,cmp);
        for(int j=1;j<=min(M,K);++j)Lst[j][i].first=Lst[j-1][i].first+Aij[i][j],Lst[j][i].second=i;
        if(K>=M)AAn[i].first.first=Lst[M][i].first;
        AAn[i].first.second=Lst[K%M][i].first;
        AAn[i].second=i;
    }
//    for(int i=1;i<=N;++i){
//    for(int j=1;j<=min(M,K);++j)printf("%d ",Lst[j][i].first);
//    printf("\n");
//    }
    sort(AAn+1,AAn+1+N);
    for(int i=1;i<=min(M,K);++i)sort(Lst[i]+1,Lst[i]+1+N);
    while(K)
    {
        if(K>=M)
        {
            int i=1;
            while(vis[AAn[i].second])++i;
            Ans+=AAn[i].first.first;
            vis[AAn[i].second]=1;
        }
        else
        {
            int i=1;
            while(vis[Lst[K][i].second])++i;
            Ans+=Lst[K][i].first;
            vis[Lst[K][i].second]=1;
        }
//        cout<<K<<" "<<Ans<<endl;
        K-=min(K,M);
    }
    printf("%lld",Ans);
    return 0;
}

为我的智商干一个凉凉杯(?
Alice想要让Bob拿到最小的,Bob想要拿到最大的
那么Alice每次会取一个“最大值最小”的行,Bob当然会取里面的最大值…
这是十分贪心的情况,因为可能会这样:
2 4 5
2 3 3 3 3
1 5 1 5 1
那就不行了。
如果只看行最大值的话,后面那些就顾及不到,也就有可能出现局部最优的情况。
进一步考虑。
为什么要放弃最大值最小的行?
考虑到从某一行里面取的值一定是单调递减的,可以把每一行排序。
放弃了行x,选择了行y≠x。
M a x { x } &lt; M a x { y } Max\{x\}&lt;Max\{y\} ,而且在某一个位置有 S u m { x } &gt; S u m { y } Sum\{x\}&gt;Sum\{y\}
当然在这一个位置行 y y S u m Sum 应该是没被选的那些行里面最小的。
那么这是哪一个位置?
如果可以取但是不取完,那么就是说其它行前面的部分比y行的最后那一部分要小。
取z行作其它行的代表。
我们把这两行按照取到的位置这么分:
(z行)ab
(y行)cd
(abcd各代表一部分值的和)
已经知道a>c,a>b,c>d
不取d那么a<d
然而a>c>d。Q.E.D
所以一定会取完K/M行。至于怎么取很简单,按照sum从小到大排个序。
可是K%M可能≠0。
剩下的K%M行呢?从剩下没被取的里面拿吗?
这个地方我打比赛的时候没想好,就真这么以为了(贪心贪傻了
假设选了行v的前K%M个
如果行v被选的话,那行v这一行就不能全取,要在后面另外全取一行u。
同样假设
(v行)op
(u行)qr
有p<o,o<q,q>r,q+r<o+p
o+q+r<o+p+q是否可能成立?
没有办法知道是否r<p。所以可能成立。
至于u是哪一行那很简单,当然是sum排序之后的第K/M+1行了。
(wannafly自闭赛

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long
int N,M,K;
ll Ans=0;
pair<ll,ll> AAn[1005]={};
ll Aij[1005]={};
bool cmp(ll a,ll b){return a>b;}
ll Lst[1005]={};
int main()
{
    scanf("%d%d%d",&N,&M,&K);
    for(int i=1;i<=N;++i)
    {
        for(int j=1;j<=M;++j)scanf("%lld",&Aij[j]);
        sort(Aij+1,Aij+1+M,cmp);
        for(int j=1;j<=M;++j)Lst[j]=Lst[j-1]+Aij[j];
        AAn[i].first=Lst[min(K,M)];
        AAn[i].second=Lst[K%M];
    }
    sort(AAn+1,AAn+1+N); int cnt=K/M; 
    if(!cnt){ printf("%lld",AAn[1].first); return 0;}
    for(int i=1;i<=cnt;++i)Ans+=AAn[i].first;
	ll tmp=Ans; 
    if(K%M)
    {
        Ans=2147483647147483647ll;
        for(int i=cnt+1;i<=N;++i)
            Ans=min(Ans,tmp+AAn[i].second);
        for(int i=1;i<=cnt;++i)
            Ans=min(Ans,tmp-AAn[i].first+AAn[i].second+AAn[cnt+1].first);
    }
    printf("%lld",Ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Estia_/article/details/83035163