atcoder新手赛:264

补题:

C - Matrix Reducing

 

暴力:

题意:给定矩阵A、B,问是否可以通过删除某行某列使A矩阵=B矩阵;输出Yes,No;

解题:

  • 首先找到与B矩阵匹配的第一个点;

  • 找到开始的行列,开始匹配

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=19;
int a[N][N],b[N][N];
int h1,w1,h2,w2;
 
bool solve(int r,int c)
{
    vector<int>ch;
    int cnt=0,idx=0;
    for(int i=c;i<w1;i++)
    {
        if(a[r][i]==b[0][idx])
        {
            ch.push_back(i);
            idx++;
        }
    }//找到符合第一行的,满足条件的列;
    if(idx!=w2)
    return false;//找不到则直接返回false;
    idx=0;//再重新从行中找;
    for(int i=r;i<h1;i++)
    {
        int count=0;//要求每一行找到匹配的w2列
        for(int j=0;j<w2;j++)
        {
            if(a[i][ch[j]]==b[idx][j])//枚举矩阵A中的列时选择符合第一行的列;
            {
                count++;
            }
        }
        if(count==w2)
        idx++;
    }
    if(idx!=h2)
    return false;
    
    return true;
}
int main()
{
    cin>>h1>>w1;
    for(int i=0;i<h1;i++)
    for(int j=0;j<w1;j++)
    cin>>a[i][j];
    cin>>h2>>w2;
    for(int i=0;i<h2;i++)
    for(int j=0;j<w2;j++)
    cin>>b[i][j];
    //首先找到符合的第一个
    for(int i=0;i<h1;i++)
    for(int j=0;j<w1;j++)
    {
        if(a[i][j]==b[0][0])
        {
            if(solve(i,j))
            {
                cout<<"Yes"<<endl;
                return 0;
            }
            
        }
     }
            cout<<"No"<<endl;
            return 0;
}

E - Blackout 2

并查集+超级源点

 

题意:n个城市、m个发电站现在有e条边,连接两个地点(任意两个地点,可以是发电站、城市);

1-n为城市、n+1->n+m的点为供电站,城市能实现供电的条件是至少连了一个发电站。

现在有q次操作,每次操作删掉e条边中的一条,问:每次操作之后,需要统计这些边到达某个发电站的城市个数;注意:每个操作永久有效;

解题:从最终状态考虑,先删去所有边,再加边;倒序枚举存答案;注意:输出;

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>PII;
const int N=1e6+10;
int p[N],cnt[N];//父节点,答案;cnt【0】;
bool st[N];//标识删除的边
int query[N];//存需要删除的边 
PII g[N];//存地图
int find(int x)
{
    if(p[x]!=x)
    p[x] =find(p[x]);
    return p[x];
}//并查集 
int main()
{
    int n,m,k;
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
        cnt[i]=1;
    }
    for(int i=n+1;i<=n+m;i++)
    {
        p[i]=0;
    }
    for(int i=1;i<=k;i++)
    {
        int u,v;
        cin>>u>>v;
        g[i].first=u;
        g[i].second=v;
    }
    int q;
    cin>>q;
    for(int i=1;i<=q;i++)
    {
        int x;
        cin>>x;
        st[x]=true;//保存需要删除的边 ,第x条线损坏 删除 
        query[i]=x;
    }
    for(int i=1;i<=k;i++)//进行初始加边操作 
    {
        int a=g[i].first;
        int b=g[i].second;
        if(!st[i])//如果一直不被删除,则加边 
        {
            a=find(a),b=find(b);
            if(a!=b)
            {
                if(a==0)
                swap(a,b);
                p[a]=b;
                cnt[b]+=cnt[a];
            }
        }
    }
    //按删除倒序进行加边
    vector<int>ans;//存答案
    for(int i=q;i>=1;i--)
    {
        int x=query[i];//存需要删除的边  
        int a=g[x].first;
        int b=g[x].second;//存两边 
        ans.push_back(cnt[0]);//已删除时的答案 
        a=find(a),b=find(b);//存完后进行加边, 
        if(a!=b)
        {
            if(a==0)
            swap(a,b);
            p[a]=b;
            cnt[b]+=cnt[a];
        }
     } 
     reverse(ans.begin(),ans.end());//答案翻转;因为倒着求的 
     for(auto s:ans)
     cout<<s<<endl;
 }

F - Monochromatic Path

dp

 

 

题意 :给定一个h*w的01矩阵,0代表白色,1代表黑色,现在给你两个操作:1、使得某一行(i行)颜色翻转代价为r(i);2、使得某一列颜色翻转(j列)颜色翻转,代价c(j);

问:最少操作几次使得从(1,1)到(h,w)存在一条路径,这条路径的颜色相同;

解题:对于每一行操作2次是无效的,且代价增大;所以,最优解对于每一行或每一列的操作次数只可能是0/1;

并且只能向下或者向右走;

dp解决:

四维数组,dp(i,j,0/1,0/1):表示走到(i,j)这个格子,对第i行操作了0/1次,对第j列操作了0/1次;

考虑状态转移:选择从(i,j)转移到(i+1,j)和(i,j+1);这样选择只需比较a(i,j)和a(i+1,j)、a(i,j+1)在操作影响下的格子颜色是否相同,考虑是否对i+1行,j+1列操作;因此可以选择异或表示颜色操作次数对颜色的变化;0^1=1;0^0=0,1^1=0;

分两种情况:向下走:设当前格子(i,j)颜色为co1,(i+1,j)的颜色为co2;co1=a(i,j)^u^v ,co2 = a (i +1 , j) ^ v; (因为对(i,j)格子进行第 i 行操作时,不会改变i+1行的颜色);

向右走:设当前格子(i,j)颜色为co1,(i,j+1)的颜色为co2;co1=a(i,j)^u^v ;co2=a(i,j)^u;

因为对co1进行列操作时,a(i,j+1)列颜色不变;

综上:我们根据co1与co2是否相等进行转移;相等则不操作;否则计算代价;

#include<bits/stdc++.h> 
using namespace std; 
typedef long long ll; 
const int N=2100; 
const ll inf=1e18; 
ll f[N][N][2][2]; //表示走到(i,j)这个格子,对r操作了0/1次,对c操作了0/1次 
int a[N][N]; 
int r[N],c[N]; //行和列的花费 
​
int main()
{
    int h,w;
    cin>>h>>w;
    for(int i=1;i<=h;i++)
scanf("%d",&r[i]); 
for(int i=1;i<=w;i++)
scanf("%d",&c[i]); 
for(int i=1;i<=h;i++) 
for(int j=1;j<=w;j++) 
scanf("%1d",&a[i][j]);
    //初始化
    for(int i=1;i<=h;i++)
    for(int j=1;j<=w;j++)
    for(int u=0;u<2;u++)
    for(int v=0;v<2;v++)
    {
        f[i][j][u][v]=inf;
     } 
     f[1][1][0][0]=0;
     f[1][1][1][0]=r[1];
     f[1][1][0][1]=c[1];
     f[1][1][1][1]=r[1]+c[1];
     for(int i=1;i<=h;i++)
     for(int j=1;j<=w;j++)
     for(int u=0;u<2;u++)
     for(int v=0;v<2;v++)
     {
        if(i+1<=h)
        {
            int co1=a[i][j]^u^v;
            int co2=a[i+1][j]^v;
            if(co1==co2)
            f[i+1][j][0][v]=min(f[i+1][j][0][v],f[i][j][u][v]);//该行不选 
            else
            f[i+1][j][1][v]=min(f[i+1][j][1][v],f[i][j][u][v]+r[i+1]);
         }
         if(j+1<=w)
         {
            int co1=a[i][j]^u^v;
            int co2=a[i][j+1]^u;
            if(co1==co2)
            f[i][j+1][u][0]=min(f[i][j+1][u][0],f[i][j][u][v]);//相等不选该列 
            else
            f[i][j+1][u][1]=min(f[i][j+1][u][1],f[i][j][u][v]+c[j+1]);
         }
     }
     ll ans=inf;
     for(int i=0;i<2;i++)
     for(int j=0;j<2;j++)
     ans=min(ans,f[h][w][i][j]); 
     cout<<ans<<endl;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值