1. 程式人生 > 其它 >11.9 正睿做題筆記

11.9 正睿做題筆記

T1

這個題的一個想法是我們首先把所有點放入堆裡,然後每次從堆中取出權值和最小的團,然後往這個團裡每次插入一個可以插的點。但是這樣放入堆裡的元素個數無法估計,會被卡空間,下面有幾種優化:

  1. 首先我們把堆換成 multiset,然後如果取出的元素個數加上集合內元素個數之和大於 \(k\),我們就可以把那些最大的刪掉。另一個優化是如果當前元素大於 \(k\) 並且當前插入值比最大值大,我們就不插入。
  2. 我們首先把所有點的權值排序,然後我們每次取出一個團,有兩種操作,第一種是去掉最大的那個點,換一個更大的點。或者我們直接往這個團里加一個能加的點中最小的點。
  3. 我們換一種思路,考慮二分這個值,然後考慮是否能找到 \(k\)
    個團,使得這些團的權值都小於二分的值,我們考慮從空集開始擴充套件,每次加上一個能加的點,需要保證加上後權值要比二分值小,否則停止擴充套件,這樣我們最多會擴充套件 \(k\) 次,然後我們可以用 __int128 來狀壓哪些點是可以加進當前團的。

程式碼用的是第一種實現方法。

程式碼:

#include<iostream>
#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#include<bitset>
#include<queue>
#define ll long long
#define N 110
using namespace std;

bitset<110> v[N];

template<typename T> inline void read(T &x){
	x=0;int flag=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag*=-1;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
	x*=flag;
}

struct Node{
	ll val;
	bitset<110> bit;
	inline Node(){}
	inline bool operator < (const Node &b)const{
		return val<b.val;
	}
};

multiset<Node> S;

int n,k,a[N],cnt;
char s[N];

inline void Init(){
	read(n);read(k);k--;
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		for(int j=1;j<=n;j++){
			v[i][j]=s[j]-'0';
		}
	}
	for(int i=1;i<=n;i++){
		Node now;now.bit.reset();
		now.bit[i]=1;now.val=a[i];
        S.insert(now);
	}
}

int main(){
	// freopen("my.in","r",stdin);
	// freopen("my.out","w",stdout);
	Init();
	while(S.size()){
		Node Top=(*S.begin());S.erase(S.begin());
        cnt++;
		if(cnt==k){
			printf("%lld",Top.val);return 0;
		}
		int posi=-1;
		for(int i=1;i<=n;i++) if(Top.bit[i]) posi=i+1;
        while(S.size()+cnt>k) S.erase(--S.end());
		for(int j=posi;j<=n;j++){
			if((v[j]&Top.bit)==Top.bit&&(!(S.size()+cnt>k&&Top.val+a[j]>=(*(--S.end())).val))){
				Top.bit[j]=1;Top.val+=a[j];
				S.insert(Top);
				Top.bit[j]=0;Top.val-=a[j];
			}
		}
	}
	puts("-1");
	return 0;
}

T2

我們首先考慮 \(a_i\) 一定是 \(b_{i-2},b_{i-1},b_i\) 中的一個值,原因是我們只是需要一個相對大小,我們總可以調整調整這個值使其變成 \(b\) 中的值,當然是有解的情況下。所以我們每個位置都有三個取值。設 \(f_{i,j,k}\) 表示 \(i\) 這個數放第 \(i\) 個取值,\(j\) 這個數放第 \(k\) 個取值,是否有解,轉移即可。程式碼實現略有技巧。

程式碼:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

inline int GetMid(int a,int b,int c){
    if(a>b) swap(a,b);if(b>c) swap(b,c);if(a>b) swap(a,b);
    return b;
}

int b[N],t,n,a[N][3],Pre[N][3][3],ans[N];
bool f[N][3][3];

inline void Clear(){
    for(int i=1;i<=n;i++){
        for(int j=0;j<=2;j++) for(int k=0;k<=2;k++){
            f[i][j][k]=0;Pre[i][j][k]=-1;
        }
    }
}

inline void Init(){
    read(n);
    for(int i=3;i<=n;i++) read(b[i]);
    b[1]=b[2]=b[3];
    b[n+1]=b[n+2]=b[n];
    for(int i=1;i<=n;i++){
        a[i][0]=b[i];a[i][1]=b[i+1];a[i][2]=b[i+2];
        // printf("a[%d][0]=%d a[%d][1]=%d a[2][%d]=%d\n",i,a[i][0],i,a[i][1],i,a[i][2]);
    }
    for(int i=1;i<=2;i++) for(int j=0;j<=2;j++) for(int k=0;k<=2;k++) f[i][j][k]=1;
}

inline void Solve(){
    for(int i=3;i<=n;i++)
        for(int j=0;j<=2;j++)
            for(int k=0;k<=2;k++){
                if(!f[i-1][k][j]) continue;
                for(int l=0;l<=2;l++){
                    if(GetMid(a[i][l],a[i-1][k],a[i-2][j])!=b[i]) continue;
                    f[i][l][k]=1;Pre[i][l][k]=j;
                    // printf("f[%d][%d][%d]=%d\n",i-1,k,j,f[i-1][k][j]);
                    // printf("f[%d][%d][%d]=%d\n",i,l,k,f[i][l][k]);
                    // printf("Pre[%d][%d][%d]=%d\n",i,l,k,Pre[i][l][k]);
                }
            }
    int nowj=-1,nowk=-1;
    for(int j=0;j<=2;j++) for(int k=0;k<=2;k++) if(f[n][j][k]){nowj=j;nowk=k;break;}
    // printf("nowj=%d nowk=%d\n",nowj,nowk);
    if(nowj==-1&&nowk==-1) return(void)puts("-1");
    int now=n;
    while(nowj!=-1&&nowk!=-1){
        ans[now]=a[now][nowj];
        // printf("a[%d][%d]=%d\n",now,nowj,a[now][nowj]);
        int Next=Pre[now][nowj][nowk];nowj=nowk;nowk=Next;now--;
        // printf("Next=%d\n",Next);
        if(Next==-1){
            ans[now]=a[now][nowk];break;
        }
    }
    ans[1]=b[1];
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);puts("");
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    memset(f,0,sizeof(f));memset(Pre,-1,sizeof(Pre));
    read(t);
    while(t--){
        Init();Solve();Clear();
    }
    return 0;
}

T3,T4 因為考得不是 NOIP 內容,所以暫且先不補了。