uva 1393 Highways

关键词:两次dp:固定端点->不固定端点、矩形dp
题意:在n*m矩阵中有多少条至少穿过两个格点的非水平非竖直的直线
解法:DP
1.求n*m矩阵中经过原点的直线条数。经过原点不经过(n,m)的直线条数是dp[n][m-1]+dp[n-1][m]-dp[n-1][m-1],同时经过原点和(n,m)的额外直线(不经过其他点)条数是gcd(n,m)==1
因此:dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+(gcd(i,j)==1)
2.求答案。i*j矩阵中不经过(i,j)的直线条数是ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1],经过(i,j)的额外直线条数是dp[i]]j]-dp[i/2][j/2](即在i*j矩阵中经过(i,j)且共经过两点的直线条数)

拓展:
1.包含水平和竖直的直线
初始化dp[0][i]和dp[i][0]都改为1,dp[0][0]=0
ans数组同理改动即可

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#define ll long long
#define sf scanf
#define pf printf
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
const ll mod=1000000007;
using namespace std;

const int maxn = 300+10;

int n,m;
ll dp[maxn][maxn];//dp[i][j]:i*j矩阵中从(0,0)点出发至少经过两个点的直线条数
ll ans[maxn][maxn];//ans[i][j]:i*j矩阵中至少经过两个点的直线条数
int vis[maxn][maxn];


int gcd(int a,int b){
    return (b==0)?a:(gcd(b,a%b));
}

int main(){
    //freopen("a.txt","r",stdin);
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=maxn;i++){
        for(int j=1;j<=maxn;j++){
            if(gcd(i,j)==1) vis[i][j]=1;
        }
    }
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0&&m==0) break;
        memset(dp,0,sizeof(dp));
        memset(ans,0,sizeof(ans));
        n--,m--;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+vis[i][j];
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]+dp[i][j]-dp[i/2][j/2];
                //dp[i][j]-dp[i/2][j/2]:i*j矩阵中从(0,0)出发仅经过两个点的直线条数
            }
        }
        printf("%lld\n",ans[n][m]*2);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值