2021-09-28

扫雷

初看题目……熟悉而简单恶心
然后看到m=2……嗯,简单起来了还是不会啊
所以,想了个暴力……W了
(其实暴力是可以过的……因为暴力加点优化很快就能回溯的,有时暴力其实是一种好方法
然后快乐摆烂

#include<bits/stdc++.h>
#include<cstdlib>
#include<ctime>
#define LL long long
using namespace std;
int random(int n){
	return (LL)rand()*rand()%n;
}
int main(){
	srand((unsigned)time(0));
	int n=random(4)+1;
	printf("%d",n/2);
}

呵呵,W55%,笑死我了
然后好好看了下,发现好简单……
其实就是:
第一个是炸弹,跑一遍;第一个不是炸弹,跑一遍……(淦)
就完了???嗯,就完了……
附上代码:

#include<bits/stdc++.h>
#include<cstdlib>
#include<ctime>
#define LL long long
using namespace std;
int f[10001],a[10001],ans=0,n;
void check(){
	bool flag=true;
	for(int i=2;i<=n+1;i++){
		a[i]=f[i-1]-a[i-1]-a[i-2];
		if(!(a[i]==1||a[i]==0)){
			flag=false;
			break;
		}
		if(i==n+1 && a[i]!=0)flag=false;
	}
	if(flag)ans++;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&f[i]);
	}
	a[1]=1;
	check();
	a[1]=0;
	check();
	printf("%d",ans);
	return 0;
}

互不侵犯

看到10这个数,秒想到状压,就是想法有点复杂,导致刚开始没有想出来,然后,没什么好说的,直接上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
long long f[20][1<<11][400];
long long use[2010],cnt=0,sum[2010];
long long n,k,ans;
using namespace std;
bool check(int x){
  return x&(x>>1);
}
int lowbit(long long x){
	return x&-x;
}
int main ()
{
	scanf("%lld%lld",&n,&k);
	f[0][0][0]=1;
	for(int i=0;i<1<<n;i++) 
	{
		if(check(i))continue;
		use[++cnt]=i;
		int t=i;
		while(t){
			sum[cnt]++;
			t-=lowbit(t);
		}
	}//优化时间,对一层的所有数的可能的情况做筛选(不相邻)
	for(int i=1;i<=n+1;i++)//枚举层数 
		for(int a=0;a<=k;a++)//枚举数量
			for(int j=1;j<=cnt;j++)//枚举此层状态
				for(int b=1;b<=cnt;b++){//上一层状态 
					if(((use[j]&use[b])==0)&&(((use[j]>>1)&use[b])==0)&&((use[j]&(use[b]>>1))==0))
					if(sum[j]<=a)
					f[i][use[j]][a]+=f[i-1][use[b]][a-sum[j]];
				}
	printf("%lld",f[n+1][0][k]);
	return 0;
}

繁忙的都市

最小生成树模板,可以用克鲁斯卡尔算法
原地学习克鲁斯卡尔.

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int m, n, u, v, c, maxn, k;
int fa[301];
int find(int x) {
    if(fa[x]!=x) 
        fa[x]=find(fa[x]);
    return fa[x];
}
void unionn(int x,int y) {
    int fx = find(x);
    int fy = find(y);
    if (fx != fy) fa[fx]=fy;
}
struct Node {
    int x, y, v;
    bool operator < (const Node &b) const {
        return v<b.v;
    }
}a[51000];
int main() {
    cin >> n >> m;
    for (int i=1; i<=m; i++) {
        cin >> u >> v >> c;
        a[i]=(Node){u, v, c};
    }
    for (int i=1; i<=n; i++) fa[i]=i;
    sort(a+1,a+m+1);
    for (int i=1; i<=m; i++) {
        if (find(fa[a[i].x]) != find(fa[a[i].y])) {
            unionn(a[i].x, a[i].y);
            maxn = a[i].v;
            k++;
        }
        if (k == n-1) break;
    }
    cout<< n-1 << " " <<maxn; 
    return 0;
}

最大子矩阵


慢慢dp吧
注意到m=1或者2:
当m=1时,是普通的最大连续字段和,只不过是k个:
设dp[i][j]表示前i个数中取出j个矩形的最大和
转移:
选:dp[i][j]=max{dp[l][j-1]+s[i]-s[l-1]}
不选:dp[i][j]=max(dp[i][j],dp[i-1][j])
当m=2时,设f[i][j][k]表示第一列选到第i个数,第二列选到第j个数时,总共k个矩形的答案
转移有4种情况
当这一位什么都不做的时候:f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k])
当仅选取第一列的某段区间时:f[i][j][k]=max(f[l][j][k-1]+sum[i][1]-sum[l-1][1]) 1<=l<i
当仅选取第二列的某段区间时:f[i][j][k]=max(f[i][l][k-1]+sum[j][2]-sum[l-1][2]) 1<=l<j
当i==j时,可以选取两列一起的f[i][j][k]=max(f[l][l][k]+sum[i][1]+sum[i][2]-sum[l-1][1]-sum[l-1][2])

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=110;
const int M=11;
int n,m,K,s1[N],s2[N],dp[N][M],f[N][N][M];
int main(){
    scanf("%d%d%d",&n,&m,&K);
    if(m==1){
        for(int i=1,x;i<=n;i++) scanf("%d",&x),s1[i]=s1[i-1]+x;
        for(int k=1;k<=K;k++){
            for(int i=1;i<=n;i++){
                dp[i][k]=dp[i-1][k];
                for(int j=0;j<i;j++) dp[i][k]=max(dp[i][k],dp[j][k-1]+s1[i]-s1[j]);
            }
        }
        printf("%d\n",dp[n][K]);
    }
    else{
        for(int i=1,x,y;i<=n;i++) scanf("%d%d",&x,&y),s1[i]=s1[i-1]+x,s2[i]=s2[i-1]+y;
        for(int k=1;k<=K;k++){
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k]);
                    for(int l=0;l<i;l++) f[i][j][k]=max(f[i][j][k],f[l][j][k-1]+s1[i]-s1[l]);
                    for(int l=0;l<j;l++) f[i][j][k]=max(f[i][j][k],f[i][l][k-1]+s2[j]-s2[l]);
                    if(i==j)  for(int l=0;l<i;l++) f[i][j][k]=max(f[i][j][k],f[l][l][k-1]+s1[i]-s1[l]+s2[j]-s2[l]);
                }
            }
        }
        printf("%d\n",f[n][n][K]);
    }
    return 0;
}

噫好,A了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值