1. 程式人生 > >[Wannafly26B] 冥土追魂 [貪心]

[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。 有Max{x}&lt;Max{y}Max\{x\}&lt;Max\{y\},而且在某一個位置有Sum{x}&gt;Sum{y}Sum\{x\}&gt;Sum\{y\} 當然在這一個位置行yySumSum應該是沒被選的那些行裡面最小的。 那麼這是哪一個位置? 如果可以取但是不取完,那麼就是說其它行前面的部分比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;
}